题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3106
题意:有若干任务,可以在CPU的A核或B核上运行,花费分别为Wa,Wb。
对于一些有关联的任务,若这两个任务不在同一个核上运行,则需要消耗额外花费cost(a,b)。
求最小花费。
算法:
对于每个任务分别向源点和汇点建边,权值为Wa和Wb。
若两个任务有关联,则在两个点间建双向边,权值为cost(a,b)。
这样对每个点来说,要不然就要割与源点相连的边,要不就要割与汇点相连的边。
并且,若两个点间之间存在边,且两个点中一个割的是与汇点相连的边,另一个割的是与源点相连的边,则这两个点之间的那条边也必须是割边。
否则S->u->v->T构成增广路
我还是第一次碰见既向源点建边又像汇点建边的模型,孤陋寡闻了。。。
一开始还很2B的拆点了。。后来发现拆了之后全连的是双向边,所以拆不拆一样的= =
#include<stdio.h> #include<string.h> #define INF 0x7f7f7f7f int p[51000],d[51000],cur[51000],gap[51000],head[51000]; int E,S,T; typedef struct { int u,v,weigh,next; }EDGE; EDGE edge[1100000]; void addedge(int u,int v,int weigh) { edge[E].v=v; edge[E].u=u; edge[E].weigh=weigh; edge[E].next=head[u]; head[u]=E++; edge[E].v=u; edge[E].u=v; edge[E].weigh=0; edge[E].next=head[v]; head[v]=E++; } int sap(int n) { int v,u,i,a,mind,ans; for(u=1;u<=n;u++) { cur[u]=head[u]; d[u]=gap[u]=0; } gap[0]=n; u=S;ans=0; memset(p,-1,sizeof(p)); while(d[S]<n) { for(i=cur[u];i!=-1;i=edge[i].next) if(edge[i].weigh&&(d[u]==d[edge[i].v]+1)) break; if(i!=-1) { cur[u]=i; int v=edge[i].v; p[v]=i; u=v; if(v==T) { int a=INF; for(;i!=-1;i=p[edge[i].u])a=a<edge[i].weigh?a:edge[i].weigh; for(v=T;v!=S;v=edge[p[v]].u) { edge[p[v]].weigh-=a; edge[p[v]^1].weigh+=a; } ans+=a; u=S; } } else { int mind=n; for(i=head[u];i!=-1;i=edge[i].next) if(edge[i].weigh)if(mind>d[edge[i].v]+1){mind=d[edge[i].v]+1;cur[u]=i;} --gap[d[u]]; if(gap[d[u]]==0)return ans; ++gap[mind]; d[u]= mind; if(u!=S) u=edge[p[u]].u; } } return ans; } int main() { int cas,n,m,i,ai,bi,val,u,v; while(scanf("%d%d",&n,&m)==2) { E=0;S=1;T=n+2; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++) { scanf("%d%d",&ai,&bi); addedge(1,i+1,ai); addedge(i+1,n+2,bi); } while(m--) { scanf("%d%d%d",&u,&v,&val); addedge(u+1,v+1,val); addedge(v+1,u+1,val); } printf("%d\n",sap(2*n+2)); } }