Total Eclipse
题目描述:
给一张无向图,每个点有一个权值,每次可以选择一个连通图的消去最小的那个值。连通的点权值减去最小值。
权值为0的点就不见了,花销为最小值。问把整个图的点权值消到0需要多少花销。
for example:
3 2
3 2 3
1 2
2 3
1与2连结 2与3连接 权值为3 2 3 开始3个点连通 花销为2 权值变为 1 0 1,去掉2点 1和3不在连通分别花销1。
所以最后结果为4。 (2+1+1)
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6763
(菜鸡的我,那场爆零了。。。太菜了)
思考:
这道题我当时试了很多办法,重大点出发合并到其他小点上,或者重小店出发。给其他大点减去一个权值。但是
都是不行的。因为单点出发就得辐射全图。如何保证这个全图就很困难。自闭3小时啥也没做出来。
后面看了题解幡然醒悟!!!!我当时想到了每次重全图减去割点(不行)。却没有逆向思维我们。每次加一个点判断
是否让原图形成一个新的连通图!!其思想都是判断该店对全图的影响,但是换种思考方式就立马变得可做了。
怎么做?
首先判断两个点是否连通最好的办法就是并查集找父亲。规定小点做父亲就行了。
其次对于添加的点做一个vis标记。确保目前在我的图中有这个点。
最后给权值排序重大点开始添加。小点能成为两个大点的联通点。
就这么简单,但是比赛时没想到。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+10; int h[N],nt[N*5],to[N*5],fa[N],num,A[N],id[N]; bool vis[N]; bool cmp(int a,int b){ return A[a]>A[b]; } void add(int u,int v){ nt[++num]=h[u]; h[u]=num; to[num]=v; } int fid(int x){ if(x == fa[x]) return x; else return fa[x]=fid(fa[x]); } int main(){ int t,n,m; scanf("%d",&t); while(t--){ num=0; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ vis[i]=h[i]=0; id[i]=fa[i]=i; scanf("%d",A+i); } sort(id+1,id+n+1,cmp);//按权值排大小 int a,b; for(int i=0;i<m;i++){ scanf("%d %d",&a,&b); add(a,b); add(b,a); } int pos = 1;//初始一个点,一个连通图 ll ps = A[id[1]],ans=0; vis[id[1]]=1;//vis置1,表示在图内。 for(int i=2;i<=n;i++){ ans+=(ps-A[id[i]])*pos; vis[id[i]]=1; pos++;//增加一个点,目前没跟新边,会增加一个独立点,也就是怎加一个图 for(int j=h[id[i]];j;j=nt[j]){ int v=to[j]; if(!vis[v]) continue;//不在图中的点不计算 int fx=fid(v); int fy=fid(id[i]); if(fx!=fy){//两个父亲不一样说明没添加该点时是不连通的 if(fx>fy) fa[fx]=fy; else fa[fy]=fx; pos--; } } ps=A[id[i]]; } printf("%lld\n",ans+ps*pos);//最后剩下的不要忘记加上 } }
这道题思考很重要,难度不大。但是思维没到那个地方。就是出不来。