这题是道神题,神就神在,它既能让你搞懂网络流及其优化,还给了你很大的优化空间。首先,说一下算法,这题就是一道最小割+简单构图的题目,增加两个节点分别表示两个CPU,第i个界点与两个CPU各连接一条,边权值分别为Ai、Bi,在后面的输入中要分别给ab和ba都连接一条权值为w的边(注意不要再连反向边了),然后用你建的图求最大流即可,这回我用的是Dinic求解。
下面便是优化,这题的优化最有学问,这次我只用了一个优化——多路增广,由于我的水平低,所以我只能讲个大概:多路增广如其名,即是用一次增广代替了多次增广,到一个节点时就一次性地把与其相邻的所有边翻出来求增广的和,如果和为零,代表它已增广不出什么了,把这个点废掉即可。这样就省去了冗余的增广。再次提醒,某超级大神牛说过,“废掉”这句话及其重要,一定不能省。
这次我用了4594MS,希望下次能分享到更多优化技巧,使我的程序跑得更快!
附属代码(第一次用CSDN,据说CSDN的代码着色挺专业的,试用一下):
#include <cstdio>
#include <string.h>
#include <queue>
#include <algorithm>
#include <utility>
using namespace std;
const int NMax=20020,MeMax=500000;
struct edge
{
int num,len;
edge *next,*rev;
}*S[NMax];
int N,M,L,level[NMax];
edge Me[MeMax];
queue<int> Q;
void Build(int x,int y,int z)
{
edge *tmp,*tmp2;
tmp=&Me[L++];
tmp->num=y;
tmp->len=z;
tmp->next=S[x];
tmp2=&Me[L++];
tmp2->num=x;
tmp2->len=0;
tmp2->next=S[y];
tmp->rev=tmp2;
tmp2->rev=tmp;
S[x]=tmp;
S[y]=tmp2;
}
void Build2(int x,int y,int z)
{
edge *tmp,*tmp2;
tmp=&Me[L++];
tmp->num=y;
tmp->len=z;
tmp->next=S[x];
tmp->rev=NULL;
S[x]=tmp;
}
bool CCT()
{
while(!Q.empty()) Q.pop();
int tmp;
memset(level,-1,sizeof(level));
level[0]=0;
Q.push(0);
while(!Q.empty())
{
tmp=Q.front();
Q.pop();
for(edge *p=S[tmp];p;p=p->next)
{
if(p->len && level[p->num]==-1)
{
level[p->num]=level[tmp]+1;
Q.push(p->num);
}
}
}
return level[N+1]!=-1;
}
int DFS(int a,int Min)
{
int tmp,tot=0;
if(a==N+1) return Min;
for(edge *p=S[a];p;p=p->next)
{
if(p->len && level[p->num]==level[a]+1 && tot<Min)
{
if(tmp=DFS(p->num,min(p->len,Min-tot)))
{
tot+=tmp;
p->len-=tmp;
if(p->rev!=NULL) p->rev->len+=tmp;
}
}
}
if(!tot) level[a]=-1;
return tot;
}
int main()
{
int tmp,ans;
int x,y,z;
while(scanf("%d%d",&N,&M)!=EOF)
{
memset(S,0,sizeof(S));
ans=0;
L=0;
for(int i=1;i<=N;i++)
{
scanf("%d%d",&x,&y);
Build(0,i,x);
Build(i,N+1,y);
}
for(int i=1;i<=M;i++)
{
scanf("%d%d%d",&x,&y,&z);
Build2(x,y,z);
Build2(y,x,z);
}
while(CCT())
{
while(tmp=DFS(0,(~0u>>1)))ans+=tmp;
}
printf("%d\n",ans);
}
return 0;
}