题意:有一台双核电脑,给多个任务,每个任务可以在两个核之一运行。分别给出每个任务在第一个核和第二个核上运行的消耗。后面的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;
}