[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();
}

[POJ 2987]最大权闭合图

【题目大意】给出每个点的权值和依赖关系,求两个东西:1、求取得最大权值时所取的最小点数。2、取得的最大权值。

【算法分析】直接最大权闭合图然后dfs求割就可以了。
不过我这个我不知道怎么证明正确,但是确实AC了。要保证正确的话,我觉得还是要放大边权。
即先B[i]=B[i]*n-1(正的B[i])B[i]=B[i]*n+1(负的B[i]),然后最后除回来取整就是答案,然后如果用少一个人,-1的数量就少了,这样就能保证是最小点数。
不过听说我这个也是正确的,只是我不会证明罢了。。。

【其它】1A
6393387 edward2 2987 Accepted 2592K 735MS G++ 2260B 2010-01-31 19:34:57
【CODE】
#include #include #include #define N 6000
#define E 500000
#define inf 0x7FFFFFFF
struct gtp{int x,y,next,c,op;}g[E];
int n,m,e;
int ls[N],fa[N],cur[N],num[N],d[N],b[N],v[N];
long long flow;

inline 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=1;i<=n;i++){
scanf("%d",&b[i]);
if (b[i]>0) addedge(0,i,b[i]);
else addedge(i,n+1,-b[i]);
}
for (int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y,inf);
}
}

void relabel(int k){
int mm=n+2;
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=inf;
for (int i=n+1;i!=0;i=g[fa[i]].x)
if (g[fa[i]].c for (int i=n+1;i!=0;i=g[fa[i]].x){
g[fa[i]].c-=nf;
g[g[fa[i]].op].c+=nf;
}
flow+=nf;
}

void sap(){
memset(d,0,sizeof(d));
memset(num,0,sizeof(num));
for (int i=0;i<=n+1;i++) cur[i]=ls[i];
num[0]=n+2; flow=0;
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 t=ls[k];t!=0;t=g[t].next)
if (g[t].c && !v[g[t].y]) dfs(g[t].y);
}

void print(){
memset(v,0,sizeof(v));
dfs(0);
int num=0; long long sum=0;
for (int i=1;i<=n;i++){
if (v[i]) num++;
if (b[i]>0) sum+=b[i];
}
printf("%d %lldn",num,sum-flow);
}

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