• 【BZOJ1937】[Shoi2004]Mst 最小生成树 KM算法(线性规划)


    【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--->边权

    题解:神题~

    显然,树边的权值一定减小,非树边的权值一定增大,所以如果非树边j覆盖了树边i,则有wj+dj>=wi-di,即di+dj>=wi-wj。所以这。。。tm是KM算法中的顶标?

    复习KM的原理,KM算法就是始终满足:对于每条边a-b,l(a)+l(b)>=v(a,b),并且所有l(a)+l(b)=v(a,b)的边构成的子图叫相等子图。并在满足上述条件下不断调整定标,使得相等子图不断扩大。

    而对于本题,让wi-wj就是边权,然后求出最优匹配既是答案。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,Cnt,nm,ans;
    int map[60][60],pa[810],pb[810],pc[810],To[110],Next[110],Val[110],Head[60],len[60];
    int fa[60],dep[60],bel[60],la[60],lb[810],va[60],vb[810],from[810],v[60][810];
    inline void Add(int a,int b,int c)
    {
    	To[Cnt]=b,Val[Cnt]=c,Next[Cnt]=Head[a],Head[a]=Cnt++;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void Dfs(int x)
    {
    	for(int i=Head[x];i!=-1;i=Next[i])	if(To[i]!=fa[x])	fa[To[i]]=x,dep[To[i]]=dep[x]+1,bel[To[i]]=Val[i],Dfs(To[i]);
    }
    void build(int a,int b,int c)
    {
    	if(dep[a]<dep[b])	swap(a,b);
    	while(dep[a]>dep[b])	v[bel[a]][nm]=max(0,len[bel[a]]-c),a=fa[a];
    	while(a!=b)	v[bel[a]][nm]=max(0,len[bel[a]]-c),v[bel[b]][nm]=max(0,len[bel[b]]-c),a=fa[a],b=fa[b];
    }
    int dfs(int x)
    {
    	va[x]=1;
    	for(int y=1;y<=nm;y++)	if(!vb[y]&&la[x]+lb[y]==v[x][y])
    	{
    		vb[y]=1;
    		if(!from[y]||dfs(from[y]))
    		{
    			from[y]=x;
    			return 1;
    		}
    	}
    	return 0;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,k,a,b;
    	memset(Head,-1,sizeof(Head));
    	for(i=1;i<=m;i++)	pa[i]=rd(),pb[i]=rd(),pc[i]=rd(),map[pa[i]][pb[i]]=map[pb[i]][pa[i]]=i;
    	for(i=1;i<n;i++)
    	{
    		a=rd(),b=rd(),Add(a,b,i),Add(b,a,i),len[i]=pc[map[a][b]],map[a][b]=map[b][a]=0;
    	}
    	dep[1]=1,Dfs(1);
    	for(i=1;i<=m;i++)	if(map[pa[i]][pb[i]])
    	{
    		nm++;
    		build(pa[i],pb[i],pc[i]);
    	}
    	for(i=1;i<=n;i++)	for(j=1;j<=nm;j++)	la[i]=max(la[i],v[i][j]);
    	for(i=1;i<=n;i++)
    	{
    		while(1)
    		{
    			memset(va,0,sizeof(va)),memset(vb,0,sizeof(vb));
    			if(dfs(i))	break;
    			int tmp=1<<30;
    			for(j=1;j<=n;j++)	if(va[j])	for(k=1;k<=nm;k++)	if(!vb[k])	tmp=min(tmp,la[j]+lb[k]-v[j][k]);
    			if(tmp==1<<30)	break;
    			for(j=1;j<=n;j++)	if(va[j])	la[j]-=tmp;
    			for(j=1;j<=nm;j++)	if(vb[j])	lb[j]+=tmp;
    		}
    	}
    	for(i=1;i<=n;i++)	ans+=la[i];
    	for(i=1;i<=nm;i++)	ans+=lb[i];
    	printf("%d",ans);
    	return 0;
    }
  • 相关阅读:
    Java使用POS打印机(无驱)
    关于在安装MySQL时报错"本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止"的解决方法
    Eclipse使用Git教程
    Android studio使用git教程
    数组
    序列化对象
    对象的三个属性
    javascript权威指南笔记--javascript语言核心(六)
    javascript权威指南笔记--javascript语言核心(五)--getter和setter属性
    javascript权威指南笔记--javascript语言核心(四)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7605120.html
Copyright © 2020-2023  润新知