bzoj1816 扑克牌
Description
你有n种牌,第i种牌的数目为ci。另外有一种特殊的牌:joker,它的数目是m。你可以用每种牌各一张来组成一套牌,也可以用一张joker和除了某一种牌以外的其他牌各一张组成1套牌。比如,当n=3时,一共有4种合法的套牌:{1,2,3}, {J,2,3}, {1,J,3}, {1,2,J}。 给出n, m和ci,你的任务是组成尽量多的套牌。每张牌最多只能用在一副套牌里(可以有牌不使用)。
Input
第一行包含两个整数n, m,即牌的种数和joker的个数。第二行包含n个整数ci,即每种牌的张数。
Output
输出仅一个整数,即最多组成的套牌数目。
Sample Input
3 4
1 2 3
1 2 3
Sample Output
3
样例解释
输入数据表明:一共有1个1,2个2,3个3,4个joker。最多可以组成三副套牌:{1,J,3}, {J,2,3}, {J,2,3},joker还剩一个,其余牌全部用完。
数据范围
50%的数据满足:2 < = n < = 5, 0 < = m < = 10^ 6, 0< = ci < = 200
100%的数据满足:2 < = n < = 50, 0 < = m, ci < = 500,000,000。
样例解释
输入数据表明:一共有1个1,2个2,3个3,4个joker。最多可以组成三副套牌:{1,J,3}, {J,2,3}, {J,2,3},joker还剩一个,其余牌全部用完。
数据范围
50%的数据满足:2 < = n < = 5, 0 < = m < = 10^ 6, 0< = ci < = 200
100%的数据满足:2 < = n < = 50, 0 < = m, ci < = 500,000,000。
二分,检验答案,注意joker的数量满足要求
确定一个组数,每种不够的肯定要用joker来补,如果joker的数量超过了组的数量,那肯定是不行的,因为一组最多一张,如果没有超出数量,那么任意一种牌,在不需要他的时候可以用joker替代,需要他的时候肯定有剩余,因为joker代替其他牌时候对他的需求肯定比他的牌数要少,这样就确保joker数量满足时成立的充要条件
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; const int maxn = 105; int n,m,s,c[maxn]; void input(){ cin>>n>>m; s = 0; for(int i = 1;i <= n;i++){ scanf("%d",&c[i]); if(s < c[i]) s = c[i]; } s += m; } bool check(int t){ int add = 0; for(int i = 1;i <= n;i++){ if(t - c[i] > 0) add += t - c[i]; if(t < add || m < add) return false; } return true; } void div(){ int lans = 0,rans = s,mans; while(lans <= rans){ mans = (lans + rans) >> 1; if(check(mans)){ lans = mans + 1; }else{ rans = mans - 1; } } if(check(mans))cout<<mans; else cout<<mans-1; } int main(){ input(); div(); return 0; }
还有一种做法,在保证joker数量满足的情况下,确保前面的需求不大于当前牌的数量就可以,如果前面的需求后面能满足,那么后面的需求前面就一定能满足
证明:
假设当前判定牌的种类的数量为j,补充此类牌后,不满足要求的牌类为i,j之前的牌的需求,牌组数t
j满足要求,即有j >= add
有add + (t - j) - (t - i)> i
add - j + i > i
j < add
与“j满足要求”的前提矛盾,等证前文的结论成立
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; const int maxn = 105; int n,m,s,c[maxn]; void input(){ cin>>n>>m; s = 0; for(int i = 1;i <= n;i++){ scanf("%d",&c[i]); if(s < c[i]) s = c[i]; } s += m; } bool check(int t){ int add = t - c[1]; if(add < 0) add = 0; for(int i = 2;i <= n;i++){ if(c[i] < add || m < add) return false; else{ if(t - c[i] > 0) add += t - c[i]; } } if(m < add) return false; return true; } void div(){ int lans = 0,rans = s,mans; while(lans <= rans){ mans = (lans + rans) >> 1; if(check(mans)){ lans = mans + 1; }else{ rans = mans - 1; } } if(check(mans))cout<<mans; else cout<<mans-1; } int main(){ input(); div(); return 0; }