http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18523
题意:n个人围成一圈,第i个人需要tm[i]个不同的礼物,相邻的两个人 不能有同样种类的礼物,第n个人和第一个人是相邻的
问最少要多少种礼物 可以满足要求 (每种礼物数量无穷多,可以重复使用)
对于n为偶数的情况 只需要 求一个最大的max(tm[i]+tm[i+1]) 显然 奇数的人选前面的部分,偶数的人选后面的部分,就永远不会有重复了
而n为奇数的情况,如果也用上面的方法,会出现一个情况,,第1个人和第n个人都是奇数,他们可能选到重复的,那么我们需要让第n-1个人 (编号是偶数) 选的礼物种类尽量和第一个人相同,因为他们不是相邻,所以可以尽量相同。。那么第n个人就有更多的礼物可以选择 使得既不和第n-1个人冲突 又不和第1个人冲突了。。。
而这样选择的方案 显然是 第一个人 选 1到 tm[1],然后后面的 编号为偶数的人尽量选靠左边的,编号为奇数选尽量靠右边的, 才会使得 第n-1个人 选的礼物ID尽量靠近左边(也就是尽量和第1个人相同)那么第n个人的选择方案就尽可能地多了。。。。 二分这个方案数。。。逼近答案
判断函数:
为了方便起见,在判断X个礼物是否可行时,我们定义 编号为1到 tm[1] 的礼物为 左半部分 剩下的为右半部分
即ll_has=tm[1],rr_has=X-tm[1];
如果编号为偶数 (尽量拿左边的)
ll[i]= min(tm[i],ll_has-ll[i-1]) //前一个人选了ll[i-1]个,那么剩余ll_has-ll[i-1]个可选,尽量选够
rr[i]=tm[i]-ll[i] //如果不能全部在“左边”选 ,则只能在“右边”选够剩下的
如果编号为奇数 (尽量拿右边的)
rr[i]=min(tm[i],rr_has-rr[i-1]) //前一个人选了rr[i-1]个,那么剩余rr_has-rr[i-1]个可选,尽量选够
ll[i]=tm[i]-rr[i]; //同理 选不够在左边选到满;
最后我们看第n个人
由于第一个人选的礼物 是1-tm[i] ,这部分全是左边的、、、也就是第n个人 完全不能选左边的
如果ll[n]==0,表示当前礼物数X 能使得 第n个人全部选到右边的,,也就是和第一个人不冲突。
由于X的下界是 max(tm[i]+tm[i+1]) 所以 相邻的两个人必然也是能满足 不冲突的条件的!
此方案合法!
其实主要就是判断第n个人和第1个人是否冲突
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <queue> #include <map> #include <set> #include <vector> using namespace std; int min(int a,int b) {return a<b?a:b;} int tm[100005]; int ll[100005]; int rr[100005]; int n; int main() { int i,j; int ok(int); while(cin>>n&&n) { for (i=1;i<=n;i++) { scanf("%d",&tm[i]); } if (n==1) { printf("%d\n",tm[1]);continue; //spj } int maxx=0; for (i=1;i<n;i++) { if (maxx<tm[i]+tm[i+1]) maxx=tm[i]+tm[i+1]; } if (maxx<tm[1]+tm[n]) maxx=tm[1]+tm[n]; int l=maxx; int r=100000*3; if (n%2) //如果偶数就直接输出maxx。不用二分找答案 while(l<r) { if (r-l==1) { if (!ok(l)) l=r; break; } int mid=(l+r)/2; if (ok(mid)) r=mid; else l=mid+1; } printf("%d\n",l); } return 0; } int ok(int x) { int i; ll[1]=tm[1]; rr[1]=0; int ll_has=ll[1]; int rr_has=x-ll[1]; for (i=2;i<=n;i++) { if (i%2==0)//偶数尽量靠左选,使得第n-1位(偶数)的礼物id尽量和第一位相同,使得第n位能尽可能有更多的礼物可选 { ll[i]=min(tm[i],ll_has-ll[i-1]); //如果不能拿够则尽量拿多点 rr[i]=tm[i]-ll[i]; //然后再从右边拿够 } else { rr[i]=min(tm[i],rr_has-rr[i-1]); ll[i]=tm[i]-rr[i]; } } if (ll[n]==0) return 1; return 0; }