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

1937: [Shoi2004]Mst 最小生成树

Time Limit: 3 Sec  Memory Limit: 64 MB
Submit: 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行,每行两个整数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--->边权

Source

[Submit][Status][Discuss]

注意到我们只可能增大树边,减小非树边,那么设每条边的改动幅度为$|d[i]|$,那么对于一条树边i和非树边j,必有$w[i]-d[i] \leqslant w[j]+d[j]$,即$w[i]-w[j] \leqslant d[i]+d[j]$。于是我们把边看作点,按是否为树边将所有边分成二分图,树边i与非树边j的边设为w[i]-w[j]。可以发现d[i]实际上就是KM算法中的顶标。所以求一次KM算法并将所有匹配相加就是答案,因为不在匹配里的d[i]直接作为0即可。

重新复习一下KM算法。先将X部分的d[x]设为$max\{w[x][y]\}$,Y部分的d[y]设为0,然后求m次增广(直到有完备匹配)。每次增广如果失败,则设$mn=min\{a[i]+b[j]-w[i][j]\}$,将所有交错树上的d[x]+=mn,d[y]-=mn。

理论依据:若由二分图中所有满足A[i]+B[j]=w[i][j]的边<i,j>构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。所以这是一个不断修改顶标并在相等子图上做完备匹配的过程。(任意i,j保证$d[i]+d[j] \geqslant w[i][j]$)。

定理:每次增广顶标和必然变小,最后一定是满足$d[i]+d[j] \geqslant w[i][j]$的最小可能。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mem(a,k) memset(a,k,sizeof(a))
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 using namespace std;
 7
 8 const int N=2000100,inf=0x3f3f3f3f;
 9 int n,m,u,v,ww,x,y,ans;
10 int mp[60][60],w[1010][1010],lk[1010],lx[1010],ly[1010],vx[1010],vy[1010],s[1010],dep[60],fa[60][12];
11 bool chk[60][60];
12 struct E{ int u,v,w;}e[1010];
13
14 void dfs(int x,int f){
15     fa[x][0]=f; dep[x]=dep[f]+1;
16     rep(i,1,10) fa[x][i]=fa[fa[x][i-1]][i-1];
17     rep(i,1,n) if (i!=f && chk[x][i]) dfs(i,x);
18 }
19
20 int LCA(int x,int y){
21     if (dep[x]<dep[y]) swap(x,y);
22     int t=dep[x]-dep[y];
23     for (int i=10; ~i; i--) if (t&(1<<i)) x=fa[x][i];
24     if (x==y) return x;
25     for (int i=10; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
26     return fa[x][0];
27 }
28
29 bool find(int x){
30     vx[x]=1;
31     rep(y,1,m) if (!vy[y]){
32         int t=lx[x]+ly[y]-w[x][y];
33         if (t==0){
34             vy[y]=1; if (lk[y]==-1 || find(lk[y])) { lk[y]=x; return 1; }
35         }else s[y]=min(s[y],t);
36     }
37     return 0;
38 }
39
40 void KM(){
41     mem(lk,-1); mem(lx,-inf); mem(ly,0);
42     rep(i,1,m) rep(j,1,m) lx[i]=max(lx[i],w[i][j]);
43     rep(x,1,m){
44         rep(i,1,m) s[i]=inf;
45         while (1){
46             mem(vx,0); mem(vy,0);
47             if (find(x)) break;
48             int d=inf;
49             rep(i,1,m) if (!vy[i]) d=min(d,s[i]);
50             rep(i,1,m) if (vx[i]) lx[i]-=d;
51             rep(i,1,m) if (vy[i]) ly[i]+=d; else s[i]-=d;
52         }
53     }
54     rep(i,1,m) if (lk[i]!=-1) ans+=w[lk[i]][i];
55 }
56
57 int main(){
58     scanf("%d%d",&n,&m);
59     rep(i,1,m) scanf("%d%d%d",&u,&v,&ww),e[i]=(E){u,v,ww},mp[u][v]=mp[v][u]=i;
60     rep(i,2,n) scanf("%d%d",&x,&y),chk[x][y]=chk[y][x]=1;
61     dfs(1,0);
62     rep(i,1,m){
63         int x=e[i].u,y=e[i].v,lca=LCA(x,y);
64         if (!chk[x][y]){
65             while (x!=lca) w[mp[x][fa[x][0]]][i]=e[mp[x][fa[x][0]]].w-e[i].w,x=fa[x][0];
66             while (y!=lca) w[mp[y][fa[y][0]]][i]=e[mp[y][fa[y][0]]].w-e[i].w,y=fa[y][0];
67         }
68     }
69     KM(); printf("%d\n",ans);
70     return 0;
71 }

好久没写最大费用最大流了发现自己完全不会写,调了整整一上午。需要注意:dis[]初始要赋为-inf,bfs()返回真的条件是dis[T]>0,其余不变。因为边里会有负值。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 6 typedef long long ll;
 7 using namespace std;
 8
 9 const int N=2000100,inf=0x3f3f3f3f;
10 int n,m,u,v,w,x,y,S,T,mn,cnt=1,ans;
11 int to[N],f[N],c[N],nxt[N],h[1010],pre[1010],dis[1010],q[N];
12 int mp[60][60],dep[60],fa[60][12];
13 bool inq[N],chk[60][60];
14 struct E{ int u,v,w;}e[1010];
15
16 void add(int u,int v,int w,int co){
17     to[++cnt]=v; f[cnt]=w; c[cnt]=co; nxt[cnt]=h[u]; h[u]=cnt;
18     to[++cnt]=u; f[cnt]=0; c[cnt]=-co; nxt[cnt]=h[v]; h[v]=cnt;
19 }
20
21 bool spfa(){
22     rep(i,0,T) dis[i]=-inf,pre[i]=-1,inq[i]=0;
23     dis[S]=0; inq[S]=1; q[1]=S;
24     for (int st=0,ed=1; st!=ed; ){
25         int x=q[++st]; inq[x]=0;
26         For(i,x) if (f[i] && dis[k=to[i]]<dis[x]+c[i]){
27             dis[k]=dis[x]+c[i]; pre[k]=i;
28             if (!inq[k]) inq[k]=1,q[++ed]=k;
29         }
30     }
31     return dis[T]>0;
32 }
33
34 void mcmf(){
35     for (ans=0; spfa(); ans+=dis[T]*mn){
36         mn=inf;
37         for (int i=pre[T]; ~i; i=pre[to[i^1]]) mn=min(mn,f[i]);
38         for (int i=pre[T]; ~i; i=pre[to[i^1]]) f[i]-=mn,f[i^1]+=mn;
39     }
40 }
41
42 void dfs(int x,int f){
43     fa[x][0]=f; dep[x]=dep[f]+1;
44     rep(i,1,10) fa[x][i]=fa[fa[x][i-1]][i-1];
45     rep(i,1,n) if (i!=f && chk[x][i]) dfs(i,x);
46 }
47
48 int LCA(int x,int y){
49     if (dep[x]<dep[y]) swap(x,y);
50     int t=dep[x]-dep[y];
51     for (int i=10; ~i; i--) if (t&(1<<i)) x=fa[x][i];
52     if (x==y) return x;
53     for (int i=10; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
54     return fa[x][0];
55 }
56
57 int main(){
58     freopen("bzoj1937.in","r",stdin);
59     freopen("bzoj1937.out","w",stdout);
60     scanf("%d%d",&n,&m);
61     rep(i,1,m) scanf("%d%d%d",&u,&v,&w),e[i]=(E){u,v,w},mp[u][v]=mp[v][u]=i;
62     rep(i,2,n) scanf("%d%d",&x,&y),chk[x][y]=chk[y][x]=1;
63     dfs(1,0); S=m+1; T=m+2;
64     rep(i,1,m){
65         int x=e[i].u,y=e[i].v;
66         if (chk[x][y]) add(S,i,1,0);
67         else{
68             add(i,T,1,0); int lca=LCA(x,y);
69             while (x!=lca) add(mp[x][fa[x][0]],i,1,e[mp[x][fa[x][0]]].w-e[i].w),x=fa[x][0];
70             while (y!=lca) add(mp[y][fa[y][0]],i,1,e[mp[y][fa[y][0]]].w-e[i].w),y=fa[y][0];
71         }
72     }
73     mcmf(); printf("%d\n",ans);
74     return 0;
75 }

原文地址:https://www.cnblogs.com/HocRiser/p/8663628.html

时间: 2024-11-10 15:54:04

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

【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

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的

hdu 3488(KM算法||最小费用最大流)

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

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$就退出,反正这

HDU 3488--Tour(KM or 费用流)

因为每个点只能经过一次 所以考虑拆点 这题有坑,有重边.. KM算法 把一个点拆成入点和出点 入点在X部,出点在Y步. 如果u,v之间有路径,就在X部的u点连接Y部的v点 求完美匹配. 当完美匹配的时候,每个点都有一个入度和一个出度,可知成环. 因为完美匹配求得是最大匹配 记得把每条边权值取相反数 #include <iostream> #include <cstring> #include <cstdio> using namespace std; const int

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

hdu3315-My Brute(费用流 or KM算法)

题目:My Brute Seaco是一个漂亮的妹子,喜欢玩一款名叫My Brute的游戏.情人节快到了,starvae和xingxing都想邀请妹子过节,但是妹子只能陪一个啊,于是两个人决定打一架,用男人的方式对决,来一场My Brute吧! 一开始两个人都有n(n<100)只宠物,每个宠物有生命值,伤害值,每次两个人各派出一只宠物,starvae可以任意确定宠物的出场顺序,xingxing不可以. 每局开始starvae先打,hp<=0的一方输. starvvae的第i个宠物(因为可以换顺序

KM算法 PK 最小费用最大流

用到了KM算法 ,发现自己没有这个模板,搜索学习一下上海大学final大神,http://www.cnblogs.com/kuangbin/p/3228861.html 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <iostream> 5 using namespace std; 6 7 /* KM算法 8 * 复杂度O(nx*nx*ny) 9 *