其他分享
首页 > 其他分享> > 抽屉原理(鸽巢原理)

抽屉原理(鸽巢原理)

作者:互联网

已知n+ 1个正整数,它们全都小于或等于2n,证明当中一定有两个数是互质的

取n个盒子,在第一个盒子我们放1和2,在第二个盒子我们放3和4,第三个盒子是放5和6,依此类推直到第n个盒子放2n-1和2n这两个数。

如果我们在n个盒子里随意抽出n+1个数。我们马上看到一定有一个盒子是被抽空的。

因此在这n+1个数中必有两个数是连续数,很明显的连续数是互质的。因此这问题就解决了!这就是利用了鸽巢原理的核心思想。

从n个数中选出几个数和为n的倍数

这个构造很牛 之前算莫队的时候也有用到这个统计方案 但是没想到会有抽屉原理在里面

可以保证连续的数一定存在解 当然选的数可以是不连续的 但是其他的方案在有限的时间内是无法算出来的

题意 :给定一个n行m列的矩阵,你最多可以从里面拿出 (m-1)行 ,使得 拿出来的这个矩阵 每列的最大值中的最小值最大。范围 n×m <= 1e5

首先,很明显,选择的行数越多越好(因为是先取max,所以更多的行数不会使得答案变差)

这个题的难点就在于行和列互相联系转换

m列 所以每列的最大值 所在行 可能有m个不同行

要求我们选择m-1行 所以必定至少有两列的最大值位于同一行 (抽屉原理 这个点位是这道题的关键)

综上,如果 n <= m-1,由于选的越多越好,把所有行都选了就是最佳答案。

如果n>=m呢,注意题目中的 n×m <=1e5

两边乘上m , m×m<= n×m <= 1e5

那么我们就可以枚举是哪两个列的最大值的位置是同一行。复杂度 大概就是n*sqrt(n)的复杂度。

当然也可以二分

void slove() {
	cin >> n >> m;
	vector<vector<int>>a;
	a.resize(n + 1);
	vector<int>mx;
	mx.resize(m + 1);
	//mx保存每一列的最大值
	for (int i = 0; i < a.size(); i++)a[i].resize(m + 1);

	for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)cin >> a[i][j];
	for (int j = 1; j <= m; j++) {
		mx[j] = 0;
		for (int i = 1; i <= n; i++)mx[j] = max(mx[j], a[i][j]);
	}

	if (n <= (m - 1)) {
		int ans = INF;
		for (int i = 1; i <= m; i++)ans = min(ans, mx[i]);
		cout << ans << endl;
		return;
	}
	int ans = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = i + 1; j <= m; j++) {
			int cur = INF;
			//枚举 i ,j 
			for (int k = 1; k <= m; k++)
				if (!(k == i || k == j))cur = min(cur, mx[k]);
			//cur 除了i j 列的 最大值中的最小
			int res = 0;
			for (int k = 1; k <= n; k++)
				res = max(res, min(a[k][i], a[k][j]));
			// 枚举每一行,是的 i ,j 列的最小值 最大
			ans = max(ans, min(cur, res));
		}
	}
	cout << ans << endl;
}

二分:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5;

vector<int> sp[maxn + 10];
int         n, m;

bool check(int x)
{
    vector<bool> f(n + 10);
    bool         ff = false;
    for (int i = 1; i <= m; i++) {
        int cnt = 0;
        for (int j = 1; j <= n; j++) {
            if (sp[i][j] >= x) {
                cnt++;
                f[j] = 1;
            }
        }
        if (cnt >= 2)
            ff = true;
    }
    if (!ff)
        return false;
    for (int i = 1; i <= n; i++) {
        if (!f[i])
            return false;
    }
    return true;
}

void solve()
{
    cin >> m >> n;
    int mx = 0;
    for (int i = 1; i <= m; i++) {
        sp[i].clear();
        sp[i].emplace_back(0);
        for (int j = 1; j <= n; j++) {
            int t;
            cin >> t;
            sp[i].emplace_back(t);
            mx = max(t, mx);
        }
    }
    int l = 0, r = mx;
    int ans = 0;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid)) {
            ans = mid;
            l   = mid + 1;
        }
        else
            r = mid - 1;
    }
    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

给定一个长度为n的序列,你可以每次选择一个数,给它加1,可以重复对一个数使用。

对于i in [0,n],是否可以使得序列的mex为i,如果可以最小需要操作多少次。不能输出-1.

很明显,对于某个i,如果原序列中的小于 i 的数的个数小于 i ,那么mex值一定不能为 i 了。

而且,后面的mex都是-1了。

解决了是否可以的问题,来思考下最小操作次数,其实也很简单,这里举个例子。

0 1 1 2 3

如果现在i 为 4 ,当前是可以的。

如果i为5,我们需要把一个1变成4 而且这个1不是随便找的空闲数 而是相差i最小的一个 这样才能保证当前操作数最小

又因为要先满足mex为1才能满足mex为2为3 所以直接从小到大处理即可

void slove() {
	cin >> n; for (int i = 0; i <= n; i++)cot[i] = 0;
	for (int i = 1; i <= n; i++) {
		int x; cin >> x;
		cot[x]++;
	}
	int pre = 0;//小于i的个数
	int cur = 0;//使得 0到 i-1 上的每个位置都有数的最少操作数
	vector<int>v;//储存多余的数
	vector<int>ans;
	for (int i = 0; i <= n; i++) {
		if (pre < i) {
			//-1的情况
			ans.push_back(-1);
			while (ans.size() < (n + 1))ans.push_back(-1);
			break;
		}
		else {
			ans.push_back(cur + cot[i]);
			//如果想要mex等于i,如果0 到 i-1上每个位置上都有数
			//且 i 上没有数
			if (cot[i] == 0 && v.size()) {
				cur += i - v.back();
				v.pop_back();
				//如果i这个位置上没有数的话,需要从前面拿一个数过来 因为插入的时候是按照从小到大插入的
               //所以每次直接取空闲数最后一个即可
			}
			else {
				for (int j = 1; j <= cot[i] - 1; j++)v.push_back(i);
				//i这个位置只需要一个数 ,cot[i] - 1 的数是多余的
			}
		}
		pre += cot[i];
	}

	for (int x : ans)cout << x << " ";
	cout << endl;
}

标签:盒子,int,最大值,个数,mex,鸽巢,原理,抽屉,mx
来源: https://www.cnblogs.com/wzxbeliever/p/16670456.html