這題是道神題,神就神在,它既能讓你搞懂網絡流及其優化,還給了你很大的優化空間。首先,說一下算法,這題就是一道最小割+簡單構圖的題目,增加兩個節點分別表示兩個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;
}