树边减,非树边加,wi表示i号边原来的边权,di表示i号边的改变量
对于一条非树边,覆盖所有树边,都要满足:wi−di≤wj+dj
得wi−wj≤di+dj
这样的话d就是KM算法里的顶标 对偶成最大权匹配
跑最大匹配即可
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
namespace KMA{
const int N=805;
int n,m,w[N][N];
int mate[N],S[N],T[N],lx[N],ly[N],sla[N];
bool match(int u)
{
S[u]=1;
for (int v=1;v<=m;v++)
{
if (T[v]) continue;
if (lx[u]+ly[v]==w[u][v])
{
T[v]=1;
if (!mate[v] || match(mate[v]))
return mate[v]=u,1;
}else
sla[v]=min(sla[v],lx[u]+ly[v]-w[u][v]);
}
return 0;
}
int KM()
{
if (n>m)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=i && j<=m;j++)
swap(w[i][j],w[j][i]);
swap(n,m);
}
cl(mate); cl(ly);
for (int i=1;i<=n;i++)
{
lx[i]=-1<<30;
for (int j=1;j<=m;j++) lx[i]=max(lx[i],w[i][j]);
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++) sla[j]=1<<30;
for (;;)
{
cl(S); cl(T);
if (match(i)) break;
int a=1<<30;
for (int j=1;j<=m;j++) if (!T[j]) a=min(a,sla[j]);
for (int j=1;j<=n;j++) if (S[j]) lx[j]-=a;
for (int j=1;j<=m;j++) if (T[j]) ly[j]+=a; else sla[j]-=a;
}
}
int ret=0;
for (int i=1;i<=m;i++)
ret+=w[mate[i]][i];
return ret;
}
}
const int N=55,K=10,M=805;
struct edge{
int u,v,w,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int w,int p){
G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;
}
int n,m;
int ln,rn;
int fat[N][K],depth[N],idx[N],wgt[N];
#define V G[p].v
inline void dfs(int u,int fa)
{
fat[u][0]=fa; depth[u]=depth[fa]+1;
for (int k=1;k<K;k++) fat[u][k]=fat[fat[u][k-1]][k-1];
for (int p=head[u];p;p=G[p].next)
if (V!=fa)
idx[V]=++rn,wgt[V]=G[p].w,dfs(V,u);
}
inline int LCA(int u,int v){
if (depth[u]<depth[v]) swap(u,v);
for (int k=K-1;~k;k--)
if ((depth[u]-depth[v])&(1<<k))
u=fat[u][k];
if (u==v) return u;
for (int k=K-1;~k;k--)
if (fat[u][k]!=fat[v][k])
u=fat[u][k],v=fat[v][k];
return fat[v][0];
}
struct edges{
int u,v,w,f;
}E[M];
inline void build(int u,int lca,int id,int w)
{
if (u==lca) return;
KMA::w[id][idx[u]]=max(0,wgt[u]-w);
build(fat[u][0],lca,id,w);
}
int main()
{
int iu,iv,iw,lca;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n); read(m);
for (int i=1;i<=m;i++)
read(E[i].u),read(E[i].v),read(E[i].w);
for (int i=1;i<n;i++)
{
read(iu); read(iv);
for (int j=1;j<=m;j++)
if ((E[j].u==iu && E[j].v==iv) || (E[j].u==iv && E[j].v==iu))
E[j].f=1,iw=E[j].w;
add(iu,iv,iw,++inum); add(iv,iu,iw,++inum);
}
dfs(1,0);
for (int i=1;i<=m;i++)
if (!E[i].f)
{
iu=E[i].u; iv=E[i].v;
lca=LCA(iu,iv);
build(iu,lca,++ln,E[i].w);
build(iv,lca,ln,E[i].w);
}
KMA::n=ln; KMA::m=rn;
printf("%d\n",KMA::KM());
return 0;
}
费用流的做法:http://blog.csdn.net/u014609452/article/details/52598726
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2){ p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
const int N=1005;
struct edge{
int u,v,next; int idx;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int idx,int p){
G[p].u=u; G[p].v=v; G[p].idx=idx; G[p].next=head[u]; head[u]=p;
}
int n,m;
const int K=21;
int fat[N][K],depth[N],fad[N];
#define V G[p].v
inline void dfs(int u,int fa){
fat[u][0]=fa; depth[u]=depth[fa]+1;
for (int k=1;k<K;k++) fat[u][k]=fat[fat[u][k-1]][k-1];
for (int p=head[u];p;p=G[p].next)
if (V!=fa)
dfs(V,u),fad[V]=G[p].idx;
}
inline int LCA(int u,int v){
if (depth[u]<depth[v]) swap(u,v);
for (int k=K-1;~k;k--)
if ((depth[u]-depth[v])&(1<<k))
u=fat[u][k];
if (u==v) return u;
for (int k=K-1;~k;k--)
if (fat[u][k]!=fat[v][k])
u=fat[u][k],v=fat[v][k];
return fat[v][0];
}
namespace MCMF{
const int N=1005;
struct edge{
int u,v,w,f,next;
}G[1000005];
int head[N],inum=1;
inline void add(int u,int v,int w,int f,int p){
G[p].u=u; G[p].v=v; G[p].w=w; G[p].f=f; G[p].next=head[u]; head[u]=p;
}
inline void link(int u,int v,int w,int f){
add(u,v,w,f,++inum); add(v,u,-w,0,++inum);
}
int S,T;
int dis[N],ins[N],pre[N];
const int NQ=1000005;
#define ad(x) ((x)+1==NQ?(x)=0:++(x))
int Q[NQ],l,r;
int Maxcost;
inline bool SPFA(){
for (int i=1;i<=T;i++) dis[i]=-1<<30,ins[i]=0,pre[i]=0;
l=-1,r=-1;
Q[ad(r)]=S; dis[S]=0; ins[S]=1;
while (l!=r){
int u=Q[ad(l)]; ins[u]=0;
for (int p=head[u];p;p=G[p].next)
if (G[p].f && dis[V]<dis[u]+G[p].w){
dis[V]=dis[u]+G[p].w; pre[V]=p;
if (!ins[V]) Q[ad(r)]=V,ins[V]=1;
}
}
if (dis[T]<=0) return false;
int minimum=1<<30;
for (int p=pre[T];p;p=pre[G[p].u])
minimum=min(minimum,G[p].f);
for (int p=pre[T];p;p=pre[G[p].u]){
G[p].f-=minimum; G[p^1].f+=minimum;
Maxcost+=minimum*G[p].w;
}
return true;
}
inline void Solve(){
while (SPFA());
}
}
int cnt;
int sx[N],sy[N],sw[N],sg[N],val[N];
int main(){
int ia,ib,iu,iv;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n); read(m);
MCMF::S=m+1; MCMF::T=m+2;
for (int i=1;i<=m;i++){
read(sx[i]); read(sy[i]); read(sw[i]);
val[i]=1;
}
for (int i=1;i<n;i++){
read(iu),read(iv);
for (int j=1;j<=m;j++)
if ((iu==sx[j]&&iv==sy[j]) || (iu==sy[j]&&iv==sx[j]))
sg[j]=1;
}
for (int i=1;i<=m;i++){
if (sg[i])
MCMF::link(MCMF::S,i,0,val[i]);
else
MCMF::link(i,MCMF::T,0,val[i]);
if (sg[i]){
add(sx[i],sy[i],i,++inum);
add(sy[i],sx[i],i,++inum);
}
}
dfs(1,0);
for (int i=1;i<=m;i++)
if (!sg[i]){
int u=sx[i],v=sy[i],lca=LCA(u,v);
while (u!=lca){
if (sw[fad[u]]>sw[i])
MCMF::link(fad[u],i,sw[fad[u]]-sw[i],1<<30);
u=fat[u][0];
}
while (v!=lca){
if (sw[fad[v]]>sw[i])
MCMF::link(fad[v],i,sw[fad[v]]-sw[i],1<<30);
v=fat[v][0];
}
}
MCMF::Solve();
printf("%d\n",MCMF::Maxcost);
return 0;
}
还有线性规划的做法