[USACO08NOV]Cheering up the Cow G
核心算法:Kruskal
题目中说:“删去尽可能多的边”。就是指保留一棵最小生成树。
构图:
我们取一小段来分析一下:
(可以把这个理解为单位元)
按照题目中的意思,走过这一张的花费为:
C[1] + L + C[2] + L + C[1]
即:C[1] + C[1] + 2L + C[2]
走过这一张的花费为:
C[1] + L(1,2) + C[2] + L(2,3) + C[3] + L(2,3) + C[2] + L(1,2) + C[1]
即:C[1] + (C[1] + 2L(1,2) + C[2]) + (C[2] + 2L(2,3) + C[3])
所以,我们可以推出:新的边的权值,为这条边 起点权值+终点权值+两倍边权
而最后的结果,还要加上一开始起点权值。 (即取点权中的最小值,因为题目中要求最小)
代码如下(细节见注释):
#include
#define MAXN 1000000
#define INF 0x3f3f3f3f
int fat[MAXN],siz[MAXN];
int c[MAXN],n,m,ans=0;
struct EDGE{int from,to,val;} e[MAXN];
//邻接矩阵
bool cmp(EDGE x,EDGE y){return x.valsiz[y]) std::swap(x,y);
fat[x]=y; siz[y]+=siz[x];
}
//并查集模板
bool kruskal()
{
int k=0;
std::sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;++i)
{
if(k==n-1) break;
if(Find(e[i].to)!=Find(e[i].from))
{
unionn(e[i].to,e[i].from);
++k; ans+=e[i].val;
}
}
return (k==n-1);
}
// kruskal模板
int main()
{
int minn=INF;
std::scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {fat[i]=i; siz[i]=1;}
//并查集初始化
for(int i=1;i<=n;++i)
{
std::scanf("%d",&c[i]);
minn=std::min(minn,c[i]);
//点权值中取最小值
}
for(int i=1;i<=m;++i)
{
std::scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].val);
e[i].val=e[i].val*2+c[e[i].from]+c[e[i].to];
//重新定义权值
}
if(kruskal()) std::printf("%d",ans+minn);
return 0;
}