扑克牌
【题目描述】
一副扑克牌有n张牌。一般你买的一副新扑克牌里除了这n张牌外还会有一些张特殊的牌,如果你不小心弄丢了n张牌中的某一张,就可以用特殊牌来代替,但是如果你弄丢两张的话就没有办法了,因为特殊牌上的图案是一样的。
现在你得到了很多扑克牌,准确来说,nn种牌你各有a1,a2,…,an张,同时你还有b张特殊牌,现在你需要从这些牌中整理出若干副牌供大家使用。整理出的一副牌可以由n种普通牌各一张组成,也可以由n−1种普通牌各一张再加一张特殊牌组成。
请你设计出一种方案,整理出尽可能多的牌。
【输入】
第一行给出n和b。
第二行给出a1,a2,…,an。
【输出】
输出最多能整理出的牌的副数。
【输入样例】
5 5 5 5 5 5 5
【输出样例】
6
【提示】
【数据规模及约定】
对于20%的数据,1≤n≤100,牌的数量小于100。
对于40%的数据,1≤n≤3000。
对于100%的数据,1≤n≤1000000,牌的数量≤10^6。
思路:
本题的思路思路为贪心+堆,首先可以确定一开始的全由n种牌组成的数目为min(a[i]),因此我们每次对全由n种牌组成的采取去掉一张换为特殊拍的策略,同时我们需要保证此刻整副扑克牌中的最小值最大,也就是我们再换牌时,需要先取出小根堆首的牌,然后将其牌数加一,并用comp此变量来表示此刻全由n种扑克牌组成的数目,由于我们每次采取这样的换牌策略,因此每个时刻comp都应该发生变化,而小根堆中的最小的元素代表的则是全由n种牌组成的个数以及由n-1种牌加一种特殊牌组成的个数的总数,这就是我们应使最小值最大的原因。而每次换牌的时候我们将其从1到comp进行循环,因为此刻剩余的全由n种牌组成的个数为comp。而循环结束的条件为进行一次操作后小根堆中最小的元素的大小没有发生变化或者是剩余的特殊牌等于零了。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<queue> 6 #include<algorithm> 7 using namespace std; 8 #define maxn 1000010 9 #define clear(a) memset(a,0,sizeof a) 10 11 int n,b,ans,comp; 12 int a[maxn]; 13 priority_queue<int>Q; 14 15 int main() 16 { 17 scanf("%d%d",&n,&b); 18 for(int i=1;i<=n;i++) 19 { 20 scanf("%d",&a[i]); 21 Q.push(-a[i]); 22 } 23 ans=comp=-Q.top(); 24 while(1) 25 { 26 int temp; 27 for(int i=1;i<=comp;i++) 28 { 29 if(!b)break; 30 int pre=-Q.top(); 31 Q.pop(); 32 b--; 33 Q.push(-(pre+1)); 34 } 35 temp=-Q.top(); 36 if(ans-temp==0)break; 37 comp=temp-ans; 38 ans=temp; 39 } 40 cout<<ans<<" "; 41 return 0; 42 }