1. 程式人生 > 實用技巧 >Codeforces Round #570 (Div. 3) G. Candy Box (hard version) (貪心,優先佇列)

Codeforces Round #570 (Div. 3) G. Candy Box (hard version) (貪心,優先佇列)

  • 題意:你有\(n\)個禮物,禮物有自己的種類,你想將它們按種類打包送人,但是打包的禮物數量必須不同(數量,與種類無關),同時,有些禮物你想自己留著,\(0\)表示你不想送人,問你在送出的禮物數量最大的同時,儘可能的使自己喜歡的留下來,輸出能送出的最大禮物數,以及這些禮物中自己不喜歡的數目.

  • 題解:首先,我們肯定要讓送出的禮物數最大,同時喜歡的最小,也就是送的禮物中,儘量讓它的\(f\)\(1\).這題考慮貪心,我們可以先對禮物數量進行排序,禮物數量相同讓\(1\)多的排在前面,全部丟進優先佇列裡,用\(cur\)記錄當前選的禮物數量,如果\(cur\)等於當前優先佇列裡拿出來的數量,由於\(cur\)

    表示上一次選的數量,所以當前禮物的數量就要\(-1\),注意!!!如果這個種類的禮物的\(f=1\)的數量和禮物數相同,那麼\(f=1\)的數量也要\(-1\),因為你全是\(1\),去掉一個,\(f=1\)當然也要減一(這不是廢話嗎?).

  • 程式碼:

    struct misaka{
    	int a;
    	int f;
    	bool operator < (const misaka  & mikoto) const {
    		if(a==mikoto.a) return f<mikoto.f;
    		return a<mikoto.a;
    	}
    }e;
    
    int t;
    int n;
    map<int,int> mp1,mp2;
    priority_queue<misaka,vector<misaka>> h;
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>t;
        while(t--){
        	cin>>n;
        	int ans1=0;
        	int ans2=0;
        	mp1.clear();
        	mp2.clear();
    
        	rep(i,1,n){
        		int x,f;
        		cin>>x>>f;
        		mp1[x]++;
        		mp2[x]+=f;
        	}
    
        	for(auto w : mp1){
        		e={w.se,mp2[w.fi]};
        		h.push(e);
        	}
    
        	int cur=-1;
    
    
        	while(!h.empty() && cur!=0){
        		misaka tmp=h.top();
        		h.pop();
    
        		int val=tmp.a;
        		int f=tmp.f;
    
        		if(cur!=val){
        			ans1+=val;
        			ans2+=f;
        			cur=val;
        		}
        		else{
        			if(val==f){
        				val--;
        				f--;
        			}
        			else val--;
        			h.push({val,f});
        		}
        	}
    
        	while(!h.empty()) h.pop();
    
        	cout<<ans1<<' '<<ans2<<'\n';
    
        }
    
        return 0;
    }