hdu6557 Justice(小根堆+并查集)

题意:

有n个砝码,每个砝码有一个权值k(i),表示重量为1/2k(i)
判断是否有可能将砝码分为两组,满足每组的重量都>=1/2
如果有解则输出方案

数据范围:n<=1e5,1<=k(i)<=1e9

解法:

有点类似二进制位,两个底位凑成一个高位。
这题只是多了一个倒数,两个1/2p可以合并为一个1/2p-1,问最后是否至少两个1/2,
开一个小根堆,将所有数丢入堆中,每次贪心地取出最小的尝试两个合并,看看最后是否至少两个1/2即可。

虽然k(i)的大小为1e9,但是n只有1e5,每次合并都会减少一个砝码,理论上最多合并1e5次,
总复杂度O(nlogn)

这题还需要输出方案,考虑并查集,直接将合并的砝码合并为一个集合就行了,非常方便。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
struct Node{
    int x,id;
    bool operator<(const Node& a)const{
        return x<a.x;
    }
};
int pre[maxm];
int n;
int ffind(int x){
    return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
    int T;scanf("%d",&T);
    int cas=1;
    while(T--){
        scanf("%d",&n);
        priority_queue<Node>q;
        for(int i=1;i<=n;i++)pre[i]=i;
        for(int i=1;i<=n;i++){
            int x;scanf("%d",&x);
            q.push({x,i});
        }
        while(q.size()>=2&&q.top().x>=2){
            Node a=q.top();q.pop();
            Node b=q.top();
            if(a.x==b.x){
                q.pop();
                int rt=pre[ffind(a.id)]=ffind(b.id);
                q.push({a.x-1,rt});
            }
        }
        printf("Case %d: ",cas++);
        if(q.size()>=2){
            puts("YES");
        }else{
            puts("NO");
            continue;
        }
        int rt=q.top().id;
        for(int i=1;i<=n;i++){
            if(ffind(i)==rt){
                printf("0");
            }else{
                printf("1");
            }
        }
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107733938
今日推荐