专栏文章

题解:AT_jsc2019_qual_e Card Collector

AT_jsc2019_qual_e题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min036ic
此快照首次捕获于
2025/12/01 18:22
3 个月前
此快照最后确认于
2025/12/01 18:22
3 个月前
查看原文

Sol

首先这类网格问题考虑经典地转化为二分图。具体地,左侧点代表各行,右侧点代表各列,把存在的点转化为连接其行列代表点的一条边。
考虑现在变成了一个什么问题,每个点都可以选一条临边并得到边权,每条边只能被选一次。那么选出来之后必然形如基环树森林,同时任一生成基环树森林也都可以找到相应方案。故而直接贪心地找最大权基环树森林即可,类似于 Kruskal 算法,按边权降序能加边就加边,容易反证法证明正确性。

Code

CPP
int n,r,c;
struct edge{int a,b;ll c;}es[N];

ll sum;
bool use[N],don[N];

int fa[N];
int find(int x){if(fa[x]!=x)fa[x]=find(fa[x]);return fa[x];}
void merge(int a,int b){a=find(a),b=find(b),fa[a]=b,don[b]|=don[a];}
bool same(int a,int b){return find(a)==find(b);}


inline void Main(){
    cin>>n>>r>>c;
    rep(i,1,n){
        cin>>es[i].a>>es[i].b>>es[i].c;
        es[i].b+=r;
    }
    rep(i,1,r+c)fa[i]=i;
    sort(es+1,es+1+n,[&](edge a,edge b){return a.c>b.c;});
    rep(i,1,n)if(!same(es[i].a,es[i].b)&&(!don[find(es[i].a)]||!don[find(es[i].b)])){
        use[i]=1;
        merge(es[i].a,es[i].b);
        sum+=es[i].c;
    }else if(same(es[i].a,es[i].b)&&!don[find(es[i].a)]){
        don[find(es[i].a)]=1;
        sum+=es[i].c;
    }
    put(sum);
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...