据说这是最小路径覆盖问题??反正我这么弱是不知道。
实际上,由于边是有方向的,所以如果对于任意一个点,只要入度=1,出度=1,就一定能保证满足题意。那么,我们只需要让每个点保证它去向的点只有1个,然后考虑它的前驱即可。
那么,如果令x表示该点是出发向其它的,x'表示到达的,则构图如下:
1.瞬移:从S->x',流量为1,费用为瞬移的花费;
2.航道x->y:从x->y',流量为1,费用为航道花费;
3.显然任意x必须要有流量才能出发,且只能出发去一个地方,故:S->x,流量为1,费用为0;
4.显然任意x'的入度为1,但上述条件无法保证,古:x'->T,流量为1,费用为0;
这样任意最大流都是一个合法的方案,最有解即最小费用最大流。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define N 60005 #define inf 1000000000 using namespace std; int n,m,tot=1,sta,ans,gol,fst[N],pnt[N],len[N],cst[N],nxt[N],d[N],e[N],h[N]; bool vis[N]; void add(int aa,int bb,int cc,int dd){ pnt[++tot]=bb; len[tot]=cc; cst[tot]=dd; nxt[tot]=fst[aa]; fst[aa]=tot; } bool spfa(){ memset(vis,0,sizeof(vis)); int i; for (i=sta; i<=gol; i++) d[i]=inf; d[sta]=0; int head=0,tail=1; h[1]=sta; while (head<tail){ head=head%(N-5)+1; int x=h[head],p; vis[x]=0; for (p=fst[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[x]+cst[p]<d[y]){ d[y]=d[x]+cst[p]; if (vis[y]) continue; vis[y]=0; tail=tail%(N-5)+1; h[tail]=y; e[y]=p; } } } return d[gol]<inf; } void updata(){ int i,tmp=inf; for (i=gol; i!=sta; i=pnt[e[i]^1]) tmp=min(tmp,len[e[i]]); ans+=tmp*d[gol]; for (i=gol; i!=sta; i=pnt[e[i]^1]){ len[e[i]]-=tmp; len[e[i]^1]+=tmp; } } int main(){ scanf("%d%d",&n,&m); int i; sta=0; gol=n<<1|1; for (i=1; i<=n; i++){ int x; scanf("%d",&x); add(0,i+n,1,x); add(i+n,0,0,-x); add(0,i,1,0); add(i,0,0,0); add(i+n,gol,1,0); add(gol,i+n,0,0); } for (i=1; i<=m; i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); if (x>y) swap(x,y); add(x,y+n,1,z); add(y+n,x,0,-z); } while (spfa()) updata(); printf("%d\n",ans); return 0; }
by lych
2016.2.3