bzoj 1937: [Shoi2004]Mst 最小生成树 KM算法
2018-01-19 17:05
459 查看
题意
1<=n<=50,1<=m<=800,1<=wi<=1000
分析
有点奇怪一个O(n3)的算法为什么跑这么快。。。做法大概就是说,把原图中的每一条边看成新图中x侧的点,把mst中的边看成新图中y侧的点。
若原图中有一条边(x,y,w),且mst上x到y路径中边的最大值大于w,那么这两条边就要相互影响。
准确的来讲,就是这两条边的代价的和不得小于他们权值差的绝对值。
设lx[i]表示mst上第i条边的代价,ly[i]表示原图中第i条边的代价,那么现在有若干个形如lx[i]+ly[j]<=lim的限制条件,要求最小化∑lx[i]+∑ly[i]
很容易发现这就是KM算法的限制条件。
那么我们只用在新图中把该边加上,然后最大权匹配即为答案。
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<map> using namespace std; const int N=805; const int inf=1000000000; int n,m,dep ,fa ,val ,cnt,last ; map<pair<int,int>,int> ma; struct edge{int to,next,w;}e[N*2]; struct data{int x,y,z;}a ; int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct Max_Val_Match { int n,m,map ,lx ,ly ,match ,slack ; bool vx ,vy ; bool aug(int x) { vx[x]=1; for (int i=1;i<=n;i++) { if (vy[i]||lx[x]+ly[i]-map[x][i]) continue; vy[i]=1; if (!match[i]||aug(match[i])) { match[i]=x; return 1; } } return 0; } int solve() { n=max(n,m); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) lx[i]=max(lx[i],map[i][j]); for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) vx[j]=vy[j]=0; if (aug(i)) continue; while (1) { for (int j=1;j<=n;j++) slack[j]=inf; for (int x=1;x<=n;x++) if (vx[x]) for (int y=1;y<=n;y++) if (!vy[y]) slack[y]=min(slack[y],lx[x]+ly[y]-map[x][y]); int mn=inf,to; for (int j=1;j<=n;j++) if (!vy[j]) mn=min(mn,slack[j]); for (int j=1;j<=n;j++) { if (vx[j]) lx[j]-=mn; if (vy[j]) ly[j]+=mn; else slack[j]-=mn,to=!slack[j]?j:to; } if (!match[to]) break; int s=match[to];vx[s]=vy[to]=1; for (int j=1;j<=n;j++) if (!vy[j]) slack[j]=min(slack[j],lx[s]+ly[j]-map[s][j]); } for (int j=1;j<=n;j++) vx[j]=vy[j]=0; aug(i); } int ans=0; for (int i=1;i<=n;i++) ans+=lx[i]+ly[i]; return ans; } }km; void addedge(int u,int v,int w) { e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].w=w;e[cnt].next=last[v];last[v]=cnt; } void dfs(int x) { dep[x]=dep[fa[x]]+1; for (int i=last[x];i;i=e[i].next) { if (e[i].to==fa[x]) continue; fa[e[i].to]=x; val[e[i].to]=e[i].w; dfs(e[i].to); } } bool check(int x,int y,int z) { int flag=0;km.n++; if (dep[x]<dep[y]) swap(x,y); while (dep[x]>dep[y]) { if (z<val[x]) flag=1,km.map[km.n][x]=val[x]-z; x=fa[x]; } while (x!=y) { if (z<val[x]) flag=1,km.map[km.n][x]=val[x]-z; if (z<val[y]) flag=1,km.map[km.n][y]=val[y]-z; x=fa[x];y=fa[y]; } return flag; } int main() { n=read();m=read(); for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); if (x>y) swap(x,y); ma[make_pair(x,y)]=z; a[i]=(data){x,y,z}; } for (int i=1;i<n;i++) { int x=read(),y=read(); if (x>y) swap(x,y); addedge(x,y,ma[make_pair(x,y)]); } dfs(1); km.m=n; for (int i=1;i<=m;i++) { int x=a[i].x,y=a[i].y,z=a[i].z; if (!check(x,y,z)) km.n--; } printf("%d",km.solve()); return 0; }
相关文章推荐
- [对偶 KM算法 生成树 || 最大费用可行流 || 线性规划] BZOJ 1937 [Shoi2004]Mst 最小生成树
- 【BZOJ1937】[Shoi2004]Mst 最小生成树 KM算法(线性规划)
- bzoj 1937: [Shoi2004]Mst 最小生成树 (KM算法)
- [BZOJ1937][SHOI2004]Mst最小生成树(KM算法,最大费用流)
- Bzoj1937:[Shoi2004]Mst 最小生成树:KM算法
- bzoj1937[Shoi2004]Mst 最小生成树
- bzoj 1937: [Shoi2004]Mst 最小生成树
- [BZOJ1937][Shoi2004]Mst 最小生成树(KM)
- BZOJ 1937[Shoi2004]Mst 最小生成树
- bzoj 1937: [Shoi2004]Mst 最小生成树 费用流
- bzoj 1937: [Shoi2004]Mst 最小生成树
- 【bzoj1937】[Shoi2004]Mst 最小生成树
- bzoj 1937: [Shoi2004]Mst 最小生成树
- bzoj1937 [Shoi2004]Mst 最小生成树(KM)
- 【KM】BZOJ1937 [Shoi2004]Mst 最小生成树
- 【bzoj1937】 Shoi2004—Mst 最小生成树
- BZOJ 1937: [Shoi2004]Mst 最小生成树 [二分图最大权匹配]
- [BZOJ 1937][Shoi2004]Mst 最小生成树
- [BZOJ1937][Shoi2004]Mst 最小生成树(KM)
- Bzoj1937 [Shoi2004]Mst 最小生成树