VP Codeforces Round 978 (Div. 2)

A. Bus to Pénjamo

题意:有n个家庭,每个家庭有\(a_i\)个人,现在有r排座位,每个座位可以坐两个人。如果一个人自己一个坐一个座位或者和自己家庭的人坐一个座位,他就会开心,问所有人都入座后最多有多少人开心。

我们肯定尽量让一个座位坐两个同一家庭的人,这样一个座位可以让两个人开心。考虑每个家庭落单人数和为cnt,并且还剩下m个座位,我们应该尽量让一个人坐一个位置,设有x个位置坐两个人,y个位置坐一个人,如果$m $$\geqslant$$ cnt $,则直接让所有人一个人坐一个座位,否则有 \(2x\) + \(y\) = cnt, \(x\) + \(y\) = m。解得\(x = cnt - m\)\(y = cnt - 2 * (cnt - m)\)

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int cnt = 0, ans = 0;
    for (int i = 0; i < n; ++ i) {
    	cnt += a[i] % 2;
    	ans += a[i] / 2;
    }

    m -= ans;
    cnt -= std::max(0, 2 * (cnt - m));
    std::cout << ans * 2 + cnt << "\n";
}

B. Kar Salesman

题意: 有n种类型的车, 每种类型的车有\(a_i\)辆, 一个顾客可以最多买\(x\)辆不同的车, 最少需要几个顾客才能买完所有车?

这x搞个小于等于10, 开始我还以为是什么模拟题.
顾客数至少为\(max(a)\), 不然有一种车不可能卖完.
顾客数至少为\(\lceil \frac{\sum_{i=1}^{n} a_i}{x} \rceil\), 因为假设每个顾客都能买\(x\)个,那么这是最少的购买次数.
那么答案就是上面两个的最大值, 因为最大值同时满足这两个的最少顾客数,并且已经是满足条件的顾客数里最小的了. 我们想象有一个有这个最大值行x列的矩阵,每个顾客一行一行的放车, 因为这个矩阵格子数一定大于等于总车辆数, 一定能放完, 并且恰好放到最后一行. 再少一行都不行.

点击查看代码
void solve() {
    int n, x;
    std::cin >> n >> x;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }
    
    i64 sum = 0, max = 0;
    for (int i = 0; i < n; ++ i) {
    	sum += a[i];
    	max = std::max(max, a[i]);
    }

    std::cout << std::max(max, (sum + x - 1) / x) << "\n";
}

C. Gerrymandering

题意: 有一个\(2\)\(n\)列的矩阵, 每个格子上的人要么投票给a, 要么投票给b, 你要给他们分成\(\frac{2n}{3}\)个块内格子数为\(3\)的联通块, 每个联通块如果有两个以上人投票给a, 那么这个连通块就会投票给a, 你要让a获得最大票数.

考虑dp, \(f[i][j]\), \(j \in \{-1, 0, 1\}\)表示第一行填到第i个位置时, 第二行填到的位置j的 i - j, 那么我们就可以考虑每种情况可以转移. 我们如果给第一行三个一起填, 那么第二行也一定是三个一起跟着填, 其他情况画图就懂了.

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<std::string> s(2);
	for (int i = 0; i < 2; ++ i) {
		std::cin >> s[i];
	}

	auto get = [&](int a, int b, int c, int d, int e, int f) -> int {
		return (s[a][b] == 'A') + (s[c][d] == 'A') + (s[e][f] == 'A') >= 2;
	};

	std::vector f(n + 1, std::vector<int>(3, -1e9));
	const int D = 1;
	f[0][0 + D] = 0;
	for (int i = 0; i < n; ++ i) {
		//f[i][-1]
		//1 0
		//0 0
		if (i && i + 3 < n) {
			f[i + 3][-1 + D] = std::max(f[i + 3][-1 + D], f[i][-1 + D] + 
					get(0, i, 0, i + 1, 0, i + 2)) + get(1, i - 1, 1, i, 1, i + 1);
		}

		if (i && i + 1 <= n) {
			f[i + 1][0 + D] = std::max(f[i + 1][0 + D], f[i][-1 + D] + get(0, i, 1, i - 1, 1, i));
		}

		//f[i][0]
		//1 0
		//1 0
		if (i + 3 <= n) {
			f[i + 3][0 + D] = std::max(f[i + 3][0 + D], f[i][0 + D] + 
					get(0, i, 0, i + 1, 0, i + 2) + get(1, i, 1, i + 1, 1, i + 2));
		}

		if (i + 1 < n) {
			f[i + 1][1 + D] = std::max(f[i + 1][1 + D], f[i][0 + D] + get(0, i, 1, i, 1, i + 1));
		}

		if (i + 2 < n) {
			f[i + 2][-1 + D] = std::max(f[i + 2][-1 + D], f[i][0 + D] + get(0, i, 0, i + 1, 1, i));
		}

		//f[i][1]
		//1 0
		//1 1
		if (i + 3 < n) {
			f[i + 3][1 + D] = std::max(f[i + 3][1 + D], f[i][1 + D] + 
					get(0, i, 0, i + 1, 0, i + 2) + get(1, i + 1, 1, i + 2, 1, i + 3));
		}
		if (i + 2 <= n) {
			f[i + 2][0 + D] = std::max(f[i + 2][0 + D], f[i][1 + D] + get(0, i, 0, i + 1, 1, i + 1));
		}
	}

	std::cout << f[n][0 + D] << "\n";
}

D1. Asesino (Easy Version)

题意:交互题,有n个人,这些人分为骑士,恶棍和骗子。你可以问某个人另一个人是不是骑士,骑士会说骑士和骗子是骑士, 恶棍只会说恶棍是骑士,骗子说骑士不是骑士,恶棍是骑士。你要在(n + 69)次询问里找到骗子。

观察发现,如果我们让两个人互相询问,除非其中有一个人是骗子,否则他们对对方的回答是一样的。那么我们可以遍历相邻的两个人,看这两个人里有没有骗子。最后随便找另外一个人和两个中的一个询问就知道谁是骗子了。总共(n + 2)次询问。

点击查看代码
int ask(int i, int j) {
	std::cout << "? " << i << " " << j << std::endl;
	int res;
	std::cin >> res;
	return res;
}

void solve() {
    int n;
    std::cin >> n;
    for (int i = 1; i < n; i += 2) {
    	if (ask(i, i + 1) != ask(i + 1, i)) {
    		int j = 1;
    		if (i == 1) {
    			j = 3;
    		}

    		if (ask(i, j) != ask(j, i)) {
    			std::cout << "! " << i << std::endl;
    		} else {	
    			std::cout << "! " << i  + 1<< std::endl;
    		}
    		return;
    	}
    }

    std::cout << "! " << n << std::endl;
}
posted @ 2025-01-15 15:57  maburb  阅读(81)  评论(0)    收藏  举报