ž给定一个带权图和图中的一棵生成树
ž可以修改边权
—目标: 使得给定的生成树是该图的MST
—要求修改边权的总量最小
可以增加、可以减少
一开始以为是最小割,可是树边与非树边可以同时改变,令人很不爽。
结果发现是htx的noip模拟题,是用km算法解决的。。。
于是又碰到了一个km算法非最优匹配应用。
设w[i]为树边,w[j]为非树边,我们只会减树边、加非树边,所以可以写出不等式(w[i]-l[i])<=(w[j]+r[j]),移项得w[i]-w[j]<=l[i]+r[j],这就很像km算法中的顶标不等式,而km算法正好能使l[i]+r[j]最小,那么问题解决。
这类问题的重要特点便是w[i]-w[j]<=l[i]+r[j],并且最小化右式,一开始我想直接用边权做顶标,套用不等式,可是没办法解决最小化问题,所以还是要抓住问题关键。
#include <cstdio>
#include <cstdlib>
#include <cstring>
int tail[50000],tail2[50000],next[50000],cc[50000],next2[50000],cost2[50000],pow[50000],sora[50000],sora2[50000],p[50000],q[50000],st[50000],lx[50000],ly[50000],vx[50000],vy[50000],d[50000],b[50000],slack[50000];
int ss,ss2,n,m;
const int oo=1073741819;
void origin(){for (int i=1;i<=n+m-1;i++) tail[i]=i,tail2[i]=i;ss=n+m-1,ss2=n+m-1;}
void link(int x,int y,int i){ss++,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,pow[ss]=i;}
void link2(int x,int y,int z){ss2++,next2[tail2[x]]=ss2,tail2[x]=ss2,sora2[ss2]=y,cost2[ss2]=z;if (z>lx[x]) lx[x]=z;}
int min(int x,int y) {return (x<y) ? x : y;}
void bfs(int x,int y,int k)
{
int h,r,i,ne,na;
memset(d,127,sizeof(d));
h=0,st[r=1]=x,d[x]=0,p[x]=0;
for (;h<r;)
{
ne=st[++h];
for (i=ne;next[i]!=0;)
{
i=next[i],na=sora[i];
if (d[ne]+1<d[na]) d[na]=d[ne]+1,p[na]=ne,q[na]=pow[i],st[++r]=na;
}
}
for (i=y;p[i];i=p[i]) link2(q[i],k,cc[q[i]]-cc[k]);
}
int find(int x)
{
if (vx[x]) return 0;
vx[x]=1;
for (int i=x,tmp,ne;next2[i]!=0;)
{
i=next2[i],ne=sora2[i];
tmp=lx[x]+ly[ne]-cost2[i];
if ((!vy[ne])&&(0==tmp))
{
vy[ne]=1;
if ((0==b[ne])||(find(b[ne])))
{
b[ne]=x;return 1;
}
}
else if (tmp<slack[ne]) slack[ne]=tmp;
}
return 0;
}
void km()
{
memset(b,0,sizeof(b));
for (int i=1,j,d;i<=n-1;i++)
{
memset(vx,0,sizeof(vx)),memset(vy,0,sizeof(vy)),memset(slack,127,sizeof(slack));
if (35==i)
d--;
for (;!find(i);)
{
d=oo;
for (j=n;j<=m;j++) if (!vy[j]) d=min(d,slack[j]);
for (j=n;j<=m;j++) if (vy[j]) ly[j]+=d;else slack[j]-=d;
for (j=1;j<=n-1;j++) if (vx[j]) lx[j]-=d;
memset(vx,0,sizeof(vx)),memset(vy,0,sizeof(vy));
}
}
}
void init()
{
int i,x,y,z;
scanf("%d%d\n",&n,&m);
origin();
for (i=1;i<=m;i++)
{ scanf("%d%d%d\n",&x,&y,&z);cc[i]=z;
if (i<=n-1) link(x,y,i),link(y,x,i);else bfs(x,y,i),ly[i]=0;
}
for (i=m+1;i<=m+n-1;i++)
for (int j=1;j<=n-1;j++) link2(j,i,0);
m=m+n-1;
km();
for (i=1;i<=n-1;i++) printf("%d\n",cc[i]-lx[i]);
for (i=n;i<=m-n+1;i++) printf("%d\n",cc[i]+ly[i]);
}
int main()
{
freopen("ti.in","r",stdin);
freopen("ti.out","w",stdout);
init();
return 0;
}