【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
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
修改方案不唯一。
【样例说明】
边(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就是边权,然后求出最优匹配既是答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #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; } |
| 欢迎来原网站坐坐! >原文链接<
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析
· 重磅发布!DeepSeek 微调秘籍揭秘,一键解锁升级版全家桶,AI 玩家必备神器!