[SPOJ 371]费用流

【题目来源】http://www.spoj.pl/problems/BOXES/

【题目大意】 N个盒子围成一圈,每个盒子里有A[I]个球,每向相邻的盒子移动一个球需要花费1的代价。求,最后所有盒子至少有一个球时花费的最小代价。

【算法分析】每个盒子看成一个点
1、连边[S,I],容量为A[I],费用为0。
2、连边[I,T],容量为1,费用为0。
3、连边[I,I+1],容量为inf,费用为1
4、连边[I,I-1],容量为inf,费用为1
求最小费用最大流即可。

【其它】1A
3230300 2010-02-01 12:41:33 cwj Boxes accepted 1.17 2.9M

C++

4.3.2

【CODE】
#include #include #include #define N 1002
#define inf 0x7FFFFFFF
struct gtp{int x,y,next,op,c,w;}g[10000];
int e,n,fa[N],d[N],ls[N],v[N],list[N],cost;

void addedge(int x,int y,int c,int w){
e++;
g[e].x=x; g[e].y=y; g[e].c=c; g[e].w=w;
g[e].op=e+1; g[e].next=ls[x]; ls[x]=e;
e++;
g[e].x=y; g[e].y=x; g[e].c=0; g[e].w=-w;
g[e].op=e-1; g[e].next=ls[y]; ls[y]=e;
}   

void init(){
memset(ls,0,sizeof(ls));
e=0;
scanf("%d",&n);
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
addedge(0,i,x,0);
addedge(i,n+1,1,0);
addedge(i,i%n+1,inf,1);
addedge(i%n+1,i,inf,1);
}
}   

bool spfa(){
for (int i=0;i<=n+1;i++){
v[i]=0;
d[i]=inf;
}   
v[0]=1; d[0]=0; list[0]=0;
int head=-1,tail=0;
while (head!=tail){
head++; if (head>=N) head=0;
for (int t=ls[list[head]];t;t=g[t].next)
if (g[t].c && d[g[t].x]+g[t].wd[g[t].y]=d[g[t].x]+g[t].w;
fa[g[t].y]=t;
if (!v[g[t].y]){
v[g[t].y]=1;
tail++; if (tail>=N) tail=0;
list[tail]=g[t].y;
}   
}
v[list[head]]=0;
}   
if (d[n+1]==inf) return false;
return true;
}   

void change(){
cost+=d[n+1];
int nf=inf;
for (int i=n+1;i;i=g[fa[i]].x)
if (g[fa[i]].cfor (int i=n+1;i;i=g[fa[i]].x){
g[fa[i]].c-=nf;
g[g[fa[i]].op].c+=nf;
}   
}   

int main(){
int tc;
scanf("%d",&tc);
for (int i=0;iinit();
cost=0;
while (spfa()) change();
printf("%dn",cost);
}   
return 0;
}   

[POJ 3422]K方格取数、费用流

【题目大意】一个人K次从左上角走到右下角(只能向下走或向右走),走过格子可以把上面的分数取掉,取掉以后该分数变为0,求走K次得到的最大分数。

【算法分析】典型的费用流,将每个格子拆点,连一条边费用为A[I,J],容量为1,另外一条边费用为0,容量为INF,剩下的不用说了。

【其它】1A
6397386 edward2 3422 Accepted 1316K 63MS G++ 2008B 2010-02-01 18:35:54 水过留痕

【CODE】
#include #include #define N 100000
#define inf 0x7FFFFFFF
struct gtp{int x,y,next,op,c,w;}g[1000000];
int n,S,T,e,ls[N],fa[N],d[N],cost,list[N],v[N];

void addedge(int x,int y,int c,int w){
e++;
g[e].x=x; g[e].y=y; g[e].c=c; g[e].w=w; g[e].op=e+1;
g[e].next=ls[x]; ls[x]=e;
e++;
g[e].x=y; g[e].y=x; g[e].c=0; g[e].w=-w; g[e].op=e-1;
g[e].next=ls[y]; ls[y]=e;
}

int pos(int x,int y){
return (x-1)*n+y;
}

void init(){
int m;
scanf("%d%d",&n,&m);
S=0; T=n*n*2+1;
addedge(S,1,m,0);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
int w;
scanf("%d",&w);
addedge(pos(i,j),pos(i,j)+n*n,1,w);
addedge(pos(i,j),pos(i,j)+n*n,inf,0);
if (i!=n) addedge(pos(i,j)+n*n,pos(i+1,j),inf,0);
if (j!=n) addedge(pos(i,j)+n*n,pos(i,j+1),inf,0);
}
addedge(n*n*2,T,inf,0);
}

bool spfa(){
for (int i=S;i<=T;i++){
d[i]=-1;
v[i]=0;
}
d[0]=0; list[0]=0; v[0]=1;
int head=-1,tail=0;
while (head!=tail){
head++; if (head>=N) head=0;
for (int t=ls[list[head]];t;t=g[t].next)
if (g[t].c && d[g[t].x]+g[t].w>d[g[t].y]){
d[g[t].y]=d[g[t].x]+g[t].w;
fa[g[t].y]=t;
if (!v[g[t].y]){
v[g[t].y]=1;
tail++; if (tail>=N) tail=0;
list[tail]=g[t].y;
}
}
v[list[head]]=0;
}
if (d[T]>-1) return true;
return false;
}

void change(){
cost+=d[T];
int nf=inf;
for (int i=T;i!=S;i=g[fa[i]].x)
if (g[fa[i]].c for (int i=T;i!=S;i=g[fa[i]].x){
g[fa[i]].c-=nf;
g[g[fa[i]].op].c+=nf;
}
}

void maxflow_mincost(){
cost=0;
while (spfa()) change();
}

int main(){
init();
maxflow_mincost();
printf("%dn",cost);
}

[SGU 326]网络流

【题目大意】有N只球队,给出他们已经赢了的场数和剩下比赛的场数(包括跨赛区和本赛区的),再给定本赛区剩下比赛的对战情况。求球队1能否得到冠军?(即球队1赢球场数位列第1,可以并列)

【算法分析】对于跨赛区的,就当他1的全赢了,其它队的全输了。对于本赛区的,与1有关的比赛肯定都当1赢了,然后就需要分配剩下的比赛给哪个队赢,使得其它球队赢球场数都<=球队1赢的场数。从这个分配上我们很容易想到网络流。
建立一个二分图,左边是比赛,右边是队伍。然后连边就脑残了。

【其它】第一次用C++的指针,居然1A,HAPPY
993680 31.01.10 18:11 edward 326 .CPP Accepted 25 ms 31 kb
【CODE】
#include #include #define N 1000
#define inf 0x7FFFFFFF
struct edge{int x,y,c;edge *op,*next;};
int d[N],num[N],w[21],r[21],a[21][21];
edge *ls[N],*cur[N],*fa[N];
int n,e,T;

void init(){
for (int i=0;iscanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&w[i]); 
for (int i=1;i<=n;i++) scanf("%d",&r[i]);   
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
}  

void relabel(int k){
int mm=T;
cur[k]=ls[k];
for (edge *t=ls[k];t;t=t->next)
if (t->c && d[t->y]d[k]=mm+1;
}   

void change(){
int nf=inf;
for (int i=T;i!=0;i=fa[i]->x)
if (fa[i]->cfor (int i=T;i!=0;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}   
}   

void sap(){
for (int i=0;i<=T;i++) cur[i]=ls[i];
int i=0;
while (d[0]for (;cur[i];cur[i]=cur[i]->next)
if (cur[i]->c && d[cur[i]->y]+1==d[i]) break;
if (!cur[i]){
if (!–num[d[i]]) break;
relabel(i);
num[d[i]]++;
if (i!=0) i=fa[i]->x;
}   
else{
fa[cur[i]->y]=cur[i];
i=cur[i]->y;
if (i==T){
change();
i=0;
}   
}   
}   
}   

void addedge(int x,int y,int c){
edge *p=new edge; edge *q=new edge;
p->x=x; p->y=y; p->c=c; p->op=q; p->next=ls[x]; ls[x]=p;
q->x=y; q->y=x; q->c=0; q->op=p; q->next=ls[y]; ls[y]=q;
}   

bool work(){
w[1]+=r[1];
for (int i=2;i<=n;i++)
if (w[i]>w[1]) return false;
T=n;
for (int i=2;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (a[i][j]){
T++;
addedge(0,T,a[i][j]);
addedge(T,i,inf);
addedge(T,j,inf);
}   
T++;
for (int i=2;i<=n;i++) addedge(i,T,w[1]-w[i]);
sap();
for (edge *t=ls[0];t;t=t->next)
if (t->c>0) return false;
return true;
}   

int main(){
init();
if (work()) printf("YESn");
else printf("NOn");
}   

[ZOJ 2587] 求最小割是否唯一、网络流**

【题目大意】给定一个有源汇的无向连通图,问你这个流网络的最小割是否唯一。

【算法分析】直接最小割,然后分别从S和T出发,沿着残余网络dfs,如果能遍历所有点,那么则该最小割是唯一的,否则那些不能遍历的点就是夹在两个方案中间的点。

【其它】1RE,1A
RE原因:好像数组没开够,但是按照计算应该是够了。。。反正开大了一倍就AC了。
2084877 2010-01-31 21:51:22 Accepted 2587 C++ 440 2156 edward
【CODE】
#include #include #include #define N 1000
#define E 100000
struct gtp{int x,y,next,op,c;}g[E];
int n,e,m,ls[N],fa[N],cur[N],d[N],num[N],S,T,v1[N],v2[N];

void addedge(int x,int y,int c){
e++;
g[e].x=x; g[e].y=y; g[e].c=c; g[e].op=e+1; g[e].next=ls[x]; ls[x]=e;
e++;
g[e].x=y; g[e].y=x; g[e].c=0; g[e].op=e-1; g[e].next=ls[y]; ls[y]=e;
}   

void init(){
e=0; memset(ls,0,sizeof(ls));
for (int i=1;i<=m;i++){
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
addedge(x,y,c);
addedge(y,x,c);
}   
}   

void relabel(int k){
int mm=n;
cur[k]=ls[k];
for (int t=ls[k];t;t=g[t].next)
if (g[t].c && d[g[t].y]d[k]=mm+1;
}   

void change(){
int nf=0x7FFFFFFF;
for (int i=T;i!=S;i=g[fa[i]].x)
if (g[fa[i]].cfor (int i=T;i!=S;i=g[fa[i]].x){
g[fa[i]].c-=nf;
g[g[fa[i]].op].c+=nf;
}   
}   

void sap(){
memset(d,0,sizeof(d));
memset(num,0,sizeof(num));
for (int i=1;i<=n;i++) cur[i]=ls[i];
num[0]=n;
int i=S;
while (d[S]for (;cur[i];cur[i]=g[cur[i]].next)
if (g[cur[i]].c && d[g[cur[i]].y]+1==d[i]) break;
if (!cur[i]){
if (!–num[d[i]]) break;
relabel(i);
num[d[i]]++;
if (i!=S) i=g[fa[i]].x;
}   
else{
fa[g[cur[i]].y]=cur[i];
i=g[cur[i]].y;
if (i==T){
change();
i=S;
}   
}   
}   
}   

void dfs1(int k){
v1[k]=1;
for (int t=ls[k];t!=0;t=g[t].next)
if (g[t].c && !v1[g[t].y]) dfs1(g[t].y);
}   

void dfs2(int k){
v2[k]=1;
for (int t=ls[k];t!=0;t=g[t].next)
if (g[t].c && !v2[g[t].y]) dfs2(g[t].y);
}   

void changegraph(){
memset(ls,0,sizeof(ls));
for (int i=1;i<=e;i++){
int tmp=g[i].x; g[i].x=g[i].y; g[i].y=tmp;
g[i].next=ls[g[i].x];
ls[g[i].x]=i;
}   
}   

bool flag(){
memset(v1,0,sizeof(v1));
memset(v2,0,sizeof(v2));
dfs1(S);
changegraph();
dfs2(T);
for (int i=1;i<=n;i++)
if (!v1[i] && !v2[i]) return false;
return true;
}   

int main(){
for (;;){
scanf("%d%d%d%d",&n,&m,&S,&T);
if (n+m+S+T==0) break;
init();
sap();
if (flag()) printf("UNIQUEn");
else printf("AMBIGUOUSn");
}   
}   

[POJ 3204] 求网络流的有效边**

【题目大意】给定一个流网络,让你求其中有多少条有效边。其中有效边的定义是:修改这条边的容量,可以使最大流增大。(只允许修改一条边)

【算法分析】如果你平常写网络流不都是写预留推进的话,这题应该难不倒你。。。
实际上呢,如果你把某一条边的容量增加了的话,如果最大流增大,那么必然是在当前流网络中能找到一条新的增广路(并且该增广路要经过你修改过的那条边)。而最大流的标志则是不能再找到增广路了。
于是我们得到这样一个算法:
选求最大流。
然后在残余网络中找满流边[X,Y](如果是非满流边你增加它容量也没用。。。因为它的容量本来就过多了)
如果在残余网络中存在路径[S,X]和[Y,T],那么这条边为有效边。

为什么?
因为如果你增加[X,Y]的容量,那么在存在增广路径[S,X]->[X,Y]->[Y,T]

这样我们就得到一个求有效边的算法。

但是如果你要Floyed的话。。。我不阻止你。。。
而我们其实存在更高效的方法,那就是先以S为起点DFS,然后将所有边反向,然后以T为起点dfs。
这样总复杂度就是O(maxflow)

【其它】1A
6393657 edward2 3204 Accepted 1752K 79MS G++ 2225B 2010-01-31 20:39:36
【CODE】
#include #include #include #define N 500
#define E 10001
struct gtp{int x,y,next,op,c;}g[E];
int n,m,e,ls[N],fa[N],cur[N],num[N],d[N],a[N][N],vs[N],vt[N],v[N];

void addedge(int x,int y,int c){
e++;
g[e].x=x; g[e].y=y; g[e].c=c; g[e].op=e+1; g[e].next=ls[x]; ls[x]=e;
e++;
g[e].x=y; g[e].y=x; g[e].c=0; g[e].op=e-1; g[e].next=ls[y]; ls[y]=e;
}

void init(){
scanf("%d%d",&n,&m);
for (int i=0;i int x,y,c;
scanf("%d%d%d",&x,&y,&c);
addedge(x,y,c);
}
}

void relabel(int k){
int mm=n;
cur[k]=ls[k];
for (int t=ls[k];t!=0;t=g[t].next)
if (g[t].c && d[g[t].y] d[k]=mm+1;
}

void change(){
int nf=0x7FFFFFFF;
for (int i=n-1;i;i=g[fa[i]].x)
if (g[fa[i]].c for (int i=n-1;i;i=g[fa[i]].x){
g[fa[i]].c-=nf;
g[g[fa[i]].op].c+=nf;
}
}

void sap(){
for (int i=0;i num[0]=n;
int i=0;
while (d[0] for (;cur[i];cur[i]=g[cur[i]].next)
if (g[cur[i]].c && d[g[cur[i]].y]+1==d[i]) break;
if (!cur[i]){
if (!–num[d[i]]) break;
relabel(i);
num[d[i]]++;
if (i!=0) i=g[fa[i]].x;
}
else{
fa[g[cur[i]].y]=cur[i];
i=g[cur[i]].y;
if (i==n-1){
change();
i=0;
}
}
}
}

void dfs(int k){
v[k]=1;
for (int i=0;i if (!v[i] && a[k][i]) dfs(i);
}

void work(){
memset(a,0,sizeof(a));
for (int i=1;i if (g[i].c) a[g[i].x][g[i].y]=1;
memset(v,0,sizeof(v));
dfs(0);
memcpy(vs,v,sizeof(v));

memset(a,0,sizeof(a));
for (int i=1;i if (g[i].c) a[g[i].y][g[i].x]=1;
memset(v,0,sizeof(v));
dfs(n-1);
memcpy(vt,v,sizeof(v));

int ans=0;
for (int i=1;i if (vs[g[i].x] && vt[g[i].y] && !g[i].c)
ans++;
printf("%dn",ans);
}

int main(){
init();
sap();
work();
}