灌水 bzoj-1601

    题目大意:给你n块地,将两块地之间连通有代价$P_{i,j}$,单独在一块地打井需要代价$C_i$,问将所有的井都有水的代价是多少。

    注释:1<=n<=300.

      想法:这种题做过一遍就好了,我们新建立一个0号节点。如果两块地之间打通就在这两个点之间连边。如果这个点单独打井就将这个点与新建节点连边,权值为打井代价。然后跑最小生成树。首先我们知道,这n块地中至少有一块地是打井的,不然就算所有的点都连通,也是没有水的。所以,这个强大的算法显然是正确的。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Node
{
	int a,b,val;
}f[1001000];
int fa[10001];
bool cmp(Node a,Node b)
{
	return a.val<b.val;
}
int find(int x)
{
	return x==fa[x]?x:(fa[x]=find(fa[x]));
}
bool merge(int x,int y)
{
	x=find(x);y=find(y);
	if(x==y) return false;
	fa[x]=y;return true;
}
int main()
{
	int n;
	scanf("%d",&n);
	int cnt=0;
	for(int i=0;i<=n;i++) fa[i]=i;
	for(int i=1;i<=n;i++)//向0号节点加边
	{
		int x;
		scanf("%d",&x);
		f[++cnt].a=0;
		f[cnt].b=i;
		f[cnt].val=x;
	}
	for(int i=1;i<=n;i++)//邻接矩阵,地与地之间的联通代价
	{
		for(int j=1;j<=n;j++)
		{
			int x;
			cin >> x;
			if(i>j)
			{
				f[++cnt].a=i;
				f[cnt].b=j;
				f[cnt].val=x;
			}
		}
	}
	sort(f+1,f+cnt+1,cmp);
	int count=0;
	int ans=0;
	for(int i=1;i<=cnt;i++)//kruskal
	{
		if(merge(f[i].a,f[i].b))
		{
			ans+=f[i].val;
			count++;
		}
		if(count==n) break;//注意,我们是在跑n+1个点的kruskal,所以count的退出条件是count==n
	}
	printf("%d\n",ans);
	return 0;
}

     小结:思路题,超级好玩的qwq