【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;
}
时间: 2024-10-11 22:10:08

【BZOJ1937】[Shoi2004]Mst 最小生成树 KM算法(线性规划)的相关文章

[BZOJ1937][SHOI2004]Mst最小生成树(KM算法,最大费用流)

1937: [Shoi2004]Mst 最小生成树 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 802  Solved: 344[Submit][Status][Discuss] Description Input 第 一行为N.M,其中 表示顶点的数目, 表示边的数目.顶点的编号为1.2.3.…….N-1.N.接下来的M行,每行三个整数Ui,Vi,Wi,表示顶点Ui与Vi之间有一条边,其权值为 Wi.所有的边在输入中会且仅会出现一次.再接着N-1

BZOJ1937 [Shoi2004]Mst 最小生成树

首先由贪心的想法知道,树边只减不加,非树边只加不减,令$w_i$表示i号边原来的边权,$d_i$表示i号边的改变量 对于一条非树边$j$连接着两个点$x$.$y$,则对于$xy$这条路径上的所有树边$i$,都要满足:$w_i - d_i \le w_j + d_j$ 移项可得$w_i -w_j \le d_i + d_j$ 于是我们发现$d[]$就是KM算法里的顶标了,直接跑最大匹配即可 1 /***************************************************

【KM】BZOJ1937 [Shoi2004]Mst 最小生成树

这道题拖了好久因为懒,结果1A了,惊讶∑( 口 || [题目大意] 给定一张n个顶点m条边的有权无向图.现要修改各边边权,使得给出n-1条边是这张图的最小生成树,代价为变化量的绝对值.求最小代价之和. [思路] 思路有点神,并不是我这种蒟蒻能够想到的XD 显然由贪心,树边必定变成wi-di,非树边必定变成wi+di (di≥0) 为了满足Mst的性质,考察一条非树边j,它加最小生成树后,必定构成一个环.对于环上的每一条树边i,有wi-di≤wj+dj,即di+dj≥wi-wj.这非常类似于KM的

BZOJ 1937: [Shoi2004]Mst 最小生成树 [二分图最大权匹配]

传送门 题意: 给一张无向图和一棵生成树,改变一些边的权值使生成树为最小生成树,代价为改变权值和的绝对值,求最小代价 线性规划的形式: $Min\quad \sum\limits_{i=1}^{m} \delta_i$ $Sat\quad $非树边边权$\ge$生成树上路径任何一条边的边权 $i$非树边$j$树边 $w_i+\delta_i \ge w_j-\delta_j$ 然后可以转化成二分图最小顶标和来求解 这里需要求二分图最大权非完美匹配,我的做法是遇到$d[t] < 0$就退出,反正这

SOJ4339 Driving Range 最小生成树 kruskal算法

典型的最小生成树 然后求最大的一条边 附上链接 http://cstest.scu.edu.cn/soj/problem.action?id=4339 需要注意的是有可能有 "IMPOSSIBLE" 的情况 这里用一个flag标记 记录所并的节点 只有flag = n时才能成功 负责就 "IMPOSSIBLE" 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring&g

最小生成树 kruskal算法简介

生成树--在一个图中的一个联通子图  使得所有的节点都被(访问) 最小生成树 (MST) 即联通子图的总代价(路程)最小 已知的一个图 有n个点 m条边 kruskal的算法如下 先对边从小到大排序 从最小的边起,不停的合并这条边的两个节点到一个集合,如果这条边的两个节点已经在一个集合里,则无视,否则形成回路(显然错误)直到所有的节点并到一个集合里 这里需要用到并查集来合并节点 1 int cmp(const int i,const int j) { 2 return w[i] < w[j];

最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小支撑树(minimum spanning tree)算法.给定一个无向图G,并且它的每条边均权值,则MST是一个包括G的所有顶点及边的子集的图,这个子集保证图是连通的,并且子集中所有边的权值之和为所有子集中最小的. 本节中介绍三种算法求解图的最小生成树:Prim算法.Kruskal算法和Boruvk

无向图最小生成树Prim算法

问题 无向图最小生成树的Prim算法.一般的实现过程,采用了常规排序.本文在用Python实现中,使用了python的堆排序模块,不仅精简代码,而且提高效率. 思路说明 假设点A,B,C,D,E,F,两点之间有连线的,以及它们的距离分别是:(A-B:7);(A-D:5);(B-C:8);(B-D:9);(B-E:7);(C-E:5);(D-E:15);(D-F:6);(E-F:8);(E-G:9);(F-G:11) 关于Prim算法的计算过程,参与维基百科的词条:[普里姆算法] 将上述点与点关系

hdoj 3488 Tour 【最小费用最大流】【KM算法】

Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 2299    Accepted Submission(s): 1151 Problem Description In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 3000