题意:有一台双核电脑,给多个任务,每个任务可以在两个核之一运行。分别给出每个任务在第一个核和第二个核上运行的消耗。后面的m行输入是给出两个任务在两个不同核上运行需要付出的额外消耗。问将所有任务做完所需要的最小耗费。
思路:把每个任务作为节点,在超级源点与任务间的连一条边,其容量为给任务在核1上运行的消耗,在该任务节点与超级汇点之间连一条边,容量为该任务在核2上运行的消耗。
在任务之间连接无向边,容量为两个任务不在同一核上运行的额外消耗。则原问题转化为图中的最小割。通过最小割=最大流转化为最大流来求。此题比较卡时间,需要用dinic算法来做,而且其中有一个优化比较惊艳(扫边每次不必重扫,而是记录上次扫到得位置即可)。
#include <cstdio> #include <cstring> #define N 20010 #define M 200005 struct edge{ int y,next,c; }e[4*(N+M)]; int layer[N],tmp[N],first[N],q[M],stack[N]; int n,m,tt; void add(int x,int y,int c){ e[tt].y = y; e[tt].c = c; e[tt].next = first[x]; first[x] = tt++; e[tt].y = x; e[tt].c = 0; e[tt].next = first[y]; first[y] = tt++; } int bfs(int s,int t){//建立层次图,分层 int i,front,rear,now; memset(layer, -1, sizeof(layer)); front = rear = -1; q[++rear] = s; layer[s] = 0; while (front < rear) { now = q[++front]; for(i = first[now];i!=-1;i=e[i].next) if(e[i].c && layer[e[i].y]==-1){ q[++rear] = e[i].y; layer[e[i].y] = layer[now]+1; if(e[i].y == t) return 1; } } return 0; } int dinic(int s,int t){//用临界表,栈中存放的不再是节点序号,而是边在结构体e数组中的序号 int i,j,k,now,res=0,top=-1; while(bfs(s,t)){//如果还能够将图分层,建立层次图 memcpy(tmp, first, sizeof(first));//建立一个临时数组,因为之后的优化就是基于first数组上面的 top = -1; now = s; while(1){ if (now == t) { //如果当前增广路径到达了汇点 int aug = 0x3fffffff; //增广量 for(i = 0;i<=top;i++) if(e[stack[i]].c < aug){ aug = e[stack[i]].c; k = i; //用于记录下次搜索的起点 } res += aug;//更新最大流 for(i = 0;i<=top;i++){ //更新图中流量和参与网络中的流量 e[stack[i]].c -= aug; e[stack[i]^1].c += aug; } top = k-1; //新栈顶 if(top == -1) now = s; else now = e[stack[top]].y; } for(i = tmp[now];i!=-1;i = tmp[now] = e[i].next)//这是优化的关键,边不必再扫一遍,只需从上次扫到的地方继续扫即可 if(layer[e[i].y]==layer[now]+1 && e[i].c > 0) break; if(i!=-1){ //如果能够增广 stack[++top] = i; now = e[i].y; }else{ if(top == -1) //再无增广路经 break; top--; //弹出栈顶 if(top == -1) now = s; else //取得上一个结点 now = e[stack[top]].y; tmp[now] = e[tmp[now]].next;//一开始没加这句话导致死循环,debug近一小时 } } } return res; } int main(){ int i,j,a,b; tt = 0; memset(first, -1, sizeof(first)); scanf("%d %d",&n,&m); for(i = 1;i<=n;i++){ scanf("%d %d",&a,&b); add(0,i,a); add(i,n+1,b); } for(i = 1;i<=m;i++){ scanf("%d %d %d",&a,&b,&j); add(a,b,j); add(b,a,j); } printf("%d\n",dinic(0,n+1)); return 0; }