题目描述
传送门
题解
KM算法
可行顶标 对于一个赋值二分图G(i,j,e,w) (i,j代表二分图的两边顶点标号,e代表边,w代表边的权值 ) 我们对两边的每一个顶点都赋予一个额外的值成cx,cy,使得对于二分图G内的所有边均有
cx[i]+cy[j]>=val[i][j]
对于任意一个匹配来说,他所有权值之和就是可行顶标之和,而可行顶标之和必然是权值之和最大的
回到这个题,对于在T中的边只会减少,对于不在T中的边只会增加。
设
e=(x,y)
,那么要求x,y路径上的说有边满足
wi−di<=wj+dj
,其中w表示边权,d表示变化量。
将式子移项,得到
di+dj>=wj−wi
,可以发现如果把wj-wi看成是权值,那么这个式子很符合KM算法中顶标的相关定义,所以我们可以通过KM算法来求解。
因为是最大权完美匹配,所以两边的点至少有一排应该是全部匹配上的,所以我们将树边和非树边分成两列,从非树边向树边连边权值为边权差,然后对于树边的那一列建立虚点,每个非树边对应一个,权值为0.
不过这道题其实也可以写单纯性。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 1003
#define inf 1000000000
using namespace std;
int tot,nxt[N],point[N],v[N],len[N],num[N],fa[N],x[N],y[N],c[N],mp[N][N],mark[N];
int n,m,pdx[N],pdy[N],cur[N],cx[N],cy[N],deep[N],val[N][N],cnt,belong[N],slack[N];
void add(int x,int y,int z,int k)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z; num[tot]=k;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; len[tot]=z; num[tot]=k;
}
void dfs(int x,int f)
{
deep[x]=deep[f]+1;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==f) continue;
fa[v[i]]=x; cur[v[i]]=i;
dfs(v[i],x);
}
}
void check(int i)
{
int x1=x[i]; int y1=y[i];
while (x1!=y1) {
if (deep[x1]>deep[y1]) {
int t=cur[x1];
val[cnt][num[t]]=len[t]-c[i];
x1=fa[x1];
}
else {
int t=cur[y1];
val[cnt][num[t]]=len[t]-c[i];
y1=fa[y1];
}
}
val[cnt][n+cnt]=0;
}
bool dfs(int x)
{
pdx[x]=1;
for (int i=1;i<=m;i++) {
if (pdy[i]) continue;
int gap=cx[x]+cy[i]-val[x][i];
if (!gap) {
pdy[i]=1;
if (!belong[i]||dfs(belong[i])) {
belong[i]=x;
return true;
}
} else slack[i]=min(gap,slack[i]);
}
return false;
}
int km()
{
memset(belong,0,sizeof(belong));
memset(cy,0,sizeof(cy));
for (int i=1;i<=n;i++) {
cx[i]=val[i][1];
for (int j=2;j<=m;j++) cx[i]=max(cx[i],val[i][j]);
}
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) slack[j]=inf;
while (true) {
memset(pdx,0,sizeof(pdx));
memset(pdy,0,sizeof(pdy));
if (dfs(i)) break;
int d=inf;
for (int j=1;j<=m;j++)
if (!pdy[j]) d=min(d,slack[j]);
for (int j=1;j<=n;j++)
if (pdx[j]) cx[j]-=d;
for (int j=1;j<=m;j++)
if (pdy[j]) cy[j]+=d;
else slack[j]-=d;
}
}
int ans=0;
for (int i=1;i<=m;i++) ans+=val[belong[i]][i];
return ans;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&c[i]),mp[x[i]][y[i]]=i;
for (int i=1;i<n;i++) {
int u,v; scanf("%d%d",&u,&v);
int t=mp[u][v]; mark[t]=1;
add(x[t],y[t],c[t],i);
}
dfs(1,0); cnt=0;
for (int i=1;i<=m;i++)
if (!mark[i]){
++cnt; check(i);
}
m=n+cnt; n=cnt;
printf("%d\n",km());
}