bzoj1863 [ZJOI2006]皇帝的烦恼

题目链接

problem

给出一个长度为(n)的环,第(i)个点需要分配(a_i)种颜色。相邻两个点不能有相同的颜色。求最少需要多少种颜色。

solution

挺巧妙的一个(dp)

显然答案具有单调性,所以我们可以先二分一个答案(x)

然后用(mn[i])表示第(i)个点与第(1)个点最少有多少个相同的颜色,用(mx[i])表示第(i)个点与第一个点最多有多少个相同的颜色。

然后考虑转移,对于(mn[i]),肯定要让(i-1)(1)的相同颜色尽量多,才可以使(i)(1)相同的颜色尽量少。然后考虑总共有(x)中颜色,在(1)(i-1)相同颜色尽量多的情况下,还有(x-(a_1-a_{i-1}+mx[i-1]))种与(1)不同的颜色。那就让(i)先用这些颜色染,不够的就要用与(1)相同的颜色了。所以就有(mn[i] = max(0,a_i-(x-a_1-a_{i-1}+mx[i-1]))

然后转移(mx[i]),同理,如果想让(1)(i)的相同颜色尽量多,那么就要让(i-1)(1)的相同颜色尽量少,(i)可以与(1)相同的部分,就是(1)的颜色数量减去(1)(i-1)颜色相同的部分。所以就有(mx[i]=min(a_i,a_1-mn[i-1]))

PS:(n=1)时应当特判!(虽然数据没有卡)

code

/*
* @Author: wxyww
* @Date:   2020-04-22 18:53:27
* @Last Modified time: 2020-04-22 19:08:17
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 200010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
int mn[N],mx[N],n,a[N];
bool check(int x) {
	mx[1] = mn[1] = a[1];
	for(int i = 2;i <= n;++i) {
		mn[i] = max(0,a[i] - (x - a[1] - a[i - 1] + mx[i - 1]));
		mx[i] = min(a[i],a[1] - mn[i - 1]);
	}
	return mn[n] == 0;
}
int main() {
	n = read();
	int l = 0,r = 100000000;

	for(int i = 1;i <= n;++i) {
		a[i] = read();
		l = max(l,a[i] + a[i - 1]);
	}
	if(n == 1) {
		cout<<a[1]<<endl;return 0;
	}
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid,r = mid - 1;
		else l = mid + 1;
	}
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/12755369.html