【bzoj1937】[Shoi2004]Mst 最小生成树

Description

Input

第一行为N、M,其中 表示顶点的数目, 表示边的数目。顶点的编号为1、2、3、……、N-1、N。接下来的M行,每行三个整数Ui,Vi,Wi,表示顶点Ui与Vi之间有一条边,其权值为Wi。所有的边在输入中会且仅会出现一次。再接着N-1行,每行两个整数Xi、Yi,表示顶点Xi与Yi之间的边是T的一条边。

Output

输出最小权值

Sample Input

6 9
1 2 2
1 3 2
2 3 3
3 4 3
1 5 1
2 6 3
4 5 4
4 6 7
5 6 6
1 3
2 3
3 4
4 5
4 6

Sample Output

8

【样例说明】

边(4,6)的权由7修改为3,代价为4
边(1,2)的权由2修改为3,代价为1
边(1,5)的权由1修改为4,代价为3
所以总代价为4+1+3=8

修改方案不唯一。

HINT

 1<=n<=50,1<=m<=800,1<=wi<=1000

n-->点数..m-->边数..wi--->边权

【解析】

st->树边->非树边->ed
把每一条非树边两端的树边找出来,如果树边比非树边小,那就连起来,流量为1,费用为权值差(我是树边-非树边)

跑一遍费用流,记得是最长路,为什么呢?因为我们要修改树边的值使它为最小生成树啊!

好好看吧,我觉得我的代码挺好理解的^-^

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int x,y,c,d,next,other;
}a[1310000];int len,last[810];
int st,ed;
void ins(int x,int y,int c,int d)
{
	int k1,k2;
	len++;k1=len;
	a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
	a[len].next=last[x];last[x]=len;
	
	len++;k2=len;
	a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;
	a[len].next=last[y];last[y]=len;
	
	a[k1].other=k2;
	a[k2].other=k1;
}
int d[810],head,tail,list[810],pre[810];
bool v[810];
int ans;
bool spfa()
{
	for(int i=1;i<=ed;i++) d[i]=-999999999;
	d[st]=0;
	memset(v,false,sizeof(v));v[st]=true;
	list[1]=st;head=1;tail=2;
	while(head!=tail)
	{
		int x=list[head];
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(a[k].c>0 && d[y]<d[x]+a[k].d)
			{
				d[y]=d[x]+a[k].d;
				pre[y]=k;
				if(v[y]==false)
				{
					v[y]=true;
					list[tail++]=y;
					if(tail==ed+1) tail=1;
				}
			}
		}
		head++;
		if(head==ed+1) head=1;
		v[x]=false;
	}
	if(d[ed]<0) return false;
	return true;
}
void get()
{
	int x=ed,k,minn=999999999;
	while(x!=st)
	{
		k=pre[x];
		minn=min(minn,a[k].c);
		x=a[k].x;
	}
	x=ed;
	while(x!=st)
	{
		k=pre[x];
		a[k].c-=minn;
		a[a[k].other].c+=minn;
		ans+=a[k].d*minn;
		x=a[k].x;
	}
}
struct bian
{
	int x,y,c,next;
}e[810];int lenn,lastt[810];
int map[51][51],num[51][51],tree[51][51];
int n,m;
struct uuu
{
	int id,a[810];
	uuu(){id=0;}
}w[810];
int f[810];
void dfs(int x,int fa)
{
	f[x]=fa;
	for(int i=1;i<=w[x].id;i++)
	{
		if(w[x].a[i]==fa) continue;
		dfs(w[x].a[i],x);
	}
	return ;
}
void build()
{
	for(int i=1;i<=m;i++)
	{
		int x=e[i].x,y=e[i].y;
		if(tree[x][y]!=-1) continue;
		dfs(y,0);
		for(;x!=y;x=f[x])
		{
			int t=map[x][f[x]]-e[i].c;
			if(t>0) ins(tree[x][f[x]],i,1,t);
		}
	}
	for(int i=1;i<=m;i++)
	{
		if(tree[e[i].x][e[i].y]!=-1) ins(st,num[e[i].x][e[i].y],1,0);
		else ins(num[e[i].x][e[i].y],ed,1,0);
	}
}
int main()
{
	int x,y,c,kk;
	scanf("%d%d",&n,&m);
	st=m+1;ed=st+1;
	memset(num,-1,sizeof(num));
	memset(tree,-1,sizeof(tree));
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&c);
		map[x][y]=c;map[y][x]=c;
		num[x][y]=i;num[y][x]=i;
		e[i].x=x;e[i].y=y;e[i].c=c;
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		tree[x][y]=tree[y][x]=num[x][y];
		w[x].id++;w[x].a[w[x].id]=y;
		w[y].id++;w[y].a[w[y].id]=x;
	}
	build();
	ans=0;
	while(spfa()==true) get();
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值