SGU 206 Roads
2013-03-31 11:03
183 查看
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=10032
题目大意
这题目略长啊…简化题意是说给你一个n个点,m条边的无向图,每条边有边权。现在要你修改一些边的边权,使得前n-1条边是最小生成树,要求总的修改量最小,输出修改后每条边的边权。
解题报告
好题。从题意看是最小生成树相关的题目,其实这道题的正解是匹配。
设xi为每条边的边权,di为每条边的修改量。很容易想到要让前n-1条边成为最小生成树,那么必然是让前n-1条边权值改小,其它的边权值改大。即对于一条权值为x1,修改量为d1的树边 和一条权值为x2,修改量为d2的非树边,有: x1-d1<=x2+d2。将这个不等式变形,即 d1+d2>=x1-x2 右边是一个常数,要让左边尽量小,即左右相等的时候。观察这个式子,会发现它和我们做km时顶标和那个式子(lx[i]+ly[j]>=w[i][j])有些相似。因此,假如有一条非树边<i,j>,我们可以把从i到j的树边和这条非树边看作二分图的左右节点a,b,连一条权值为x[a]-x[b]的边。把图建出来再做一次km就可以了。
图论模型真心各种灵活,太考想象力了……
代码
View Code
题目大意
这题目略长啊…简化题意是说给你一个n个点,m条边的无向图,每条边有边权。现在要你修改一些边的边权,使得前n-1条边是最小生成树,要求总的修改量最小,输出修改后每条边的边权。
解题报告
好题。从题意看是最小生成树相关的题目,其实这道题的正解是匹配。
设xi为每条边的边权,di为每条边的修改量。很容易想到要让前n-1条边成为最小生成树,那么必然是让前n-1条边权值改小,其它的边权值改大。即对于一条权值为x1,修改量为d1的树边 和一条权值为x2,修改量为d2的非树边,有: x1-d1<=x2+d2。将这个不等式变形,即 d1+d2>=x1-x2 右边是一个常数,要让左边尽量小,即左右相等的时候。观察这个式子,会发现它和我们做km时顶标和那个式子(lx[i]+ly[j]>=w[i][j])有些相似。因此,假如有一条非树边<i,j>,我们可以把从i到j的树边和这条非树边看作二分图的左右节点a,b,连一条权值为x[a]-x[b]的边。把图建出来再做一次km就可以了。
图论模型真心各种灵活,太考想象力了……
代码
View Code
#include<cstdio> #include<cstdlib> #include<cstring> #include<bitset> using namespace std; #define maxn 100 #define maxm 500 int n,m,w[maxm][maxm],g[maxn][maxn],lx[maxm],ly[maxm],l[maxm],a[maxm],b[maxm],z[maxm],slack[maxm]; bitset <maxm> ux,uy; bool find(int x) { ux[x]=1; for (int i=1;i<=m;i++) { if (!uy[i]) { int t=lx[x]+ly[i]-w[x][i]; if (t==0) { uy[i]=1; if (!l[i] || find(l[i])) { l[i]=x; return 1; } } else if (slack[i]>t) slack[i]=t; } } return 0; } void km() { memset(lx,-0x3f,sizeof(lx)); memset(ly,0,sizeof(ly)); memset(l,0,sizeof(l)); for (int i=1;i<=m;i++) for (int j=1;j<=m;j++) if (w[i][j]>lx[i]) lx[i]=w[i][j]; for (int j=1;j<=m;j++) { memset(slack,0x3f,sizeof(slack)); while (1) { ux.reset(); uy.reset(); if (find(j)) break; int d=0x3f3f3f3f; for (int i=1;i<=m;i++) if (!uy[i] && slack[i]<d) d=slack[i]; for (int i=1;i<=m;i++) if (ux[i]) lx[i]-=d; for (int i=1;i<=m;i++) { if (uy[i]) ly[i]+=d; else slack[i]-=d; } } } } bool dfs(int pre,int u,int v,int id) { if (u==v) return 1; for (int i=1;i<=n;i++) { if (i==pre || !g[u][i]) continue; if (dfs(u,i,v,id)) { w[g[u][i]][id]=z[g[u][i]]-z[id]; return 1; } } return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d%d",&x,&y,&z[i]); a[i]=x,b[i]=y; } for (int i=1;i<n;i++) g[a[i]][b[i]]=g[b[i]][a[i]]=i; memset(w,0,sizeof(w)); for (int i=n;i<=m;i++) dfs(0,a[i],b[i],i); km(); for (int i=1;i<n;i++) printf("%d\n",z[i]-lx[i]); for (int i=n;i<=m;i++) printf("%d\n",z[i]+ly[i]); return 0; }
相关文章推荐
- sgu-206 Roads
- SGU 206 Roads(KM)
- SGU 206 Roads
- SGU 206 Roads KM算法 匹配模型的转化
- Sleepers (Episode 206)
- sgu 249 Matrix
- 计算阶乘之和 分类: java 2009-11-02 14:12 206人阅读 评论(0) 收藏
- SGU 103 Traffic Lights
- Constructing Roads In JGShining's Kingdom
- 几何概率——SGU144 Meeting
- SGU 110 Dungeon 翻译
- poj-1251 Jungle Roads *
- SGU111 Very simple problem(大数开方)
- UVA 10308 Roads in the North
- sgu 185 Two shortest 最大流
- sgu175---pascal
- SGU 111 Very simple problem
- SGU 126 Boxes
- SGU105 Div 3
- sgu131