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

[POJ 1966] 网络流、求无向图的点连通度

【题目大意】给定一个无向图,让你求最小去掉多少个点以后使得原图不连通。

【算法分析】如果是有源汇的话就简单了,直接拆点然后求最小割就可以。于是这题就枚举源汇求最小割就行了。

【其它】1A
6393220 edward2 1966 Accepted 396K 125MS G++ 2212B 2010-01-31 18:25:01
【CODE】
#include #include #include #define inf 200
struct gtp{int x,y,next,c,op;}g[10000];
int n,m,e,a[50][50],S,T,flow;
int ls[100],cur[100],fa[100],d[100],num[100];

void relabel(int k){
int mm=n+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=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;
}
flow+=nf;
}

void sap(){
flow=0;
memset(d,0,sizeof(d));
memset(num,0,sizeof(num));
for (int i=0;i num[0]=n+n;
int i=S;
while (d[0] for (;cur[i]!=0;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]]==0) 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;
}
}
}
}

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;
}

int main(){
while (scanf("%d%d",&n,&m)!=EOF){
memset(a,0,sizeof(a));
for (int i=1;i<=m;i++){
int x,y;
scanf(" (%d,%d)",&x,&y);
a[x][y]=1; a[y][x]=1;
}
int ans=inf;
for (S=n;S<2*n;S++)
for (T=0;T if (S-n!=T){
e=0;
memset(ls,0,sizeof(ls));
for (int i=0;i for (int j=0;j if (a[i][j]) addedge(i+n,j,inf);
for (int i=0;i sap();
if (flow }
if (ans>n) ans=n;
printf("%dn",ans);
}
return 0;
}

[POJ1815] 网络流、求最小点割

【题目大意】给你N个点,源点和汇点,然后给定一个邻接矩阵,让你求从源到汇的一个字典序最小的点割集。

【算法分析】一个点拆成两个点,图中的边设成0x7FFFFFFF,然后一个点管出度,一个点管入度。然后就变成了求这个网络的最小割。由于题目要求字典序最小,所以必须枚举删点。

【其他】WA了1次,因为NO ANSWER的其中一个情况漏掉了。
6390962 edward2 1815 Accepted 864K 579MS G++ 2568B 2010-01-30 23:22:14
【CODE】
#include #include #include #define E 100000
#define N 401
struct edge{int x,y,op,c,next;}g[E];
int n,S,T,e,a[N][N];
int fa[N],cur[N],num[N],d[N],ls[N],v[N];

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

int change(){
int nf=0x7FFFFFFF;
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;
}
return nf;
}

int sap(){
int FF=0;
memset(d,0,sizeof(d));
memset(num,0,sizeof(num));
for (int i=1;i<=2*n;i++) cur[i]=ls[i];
num[0]=2*n;
int i=S;
while (d[S]<2*n){
for (;cur[i]!=0;cur[i]=g[cur[i]].next)
if (g[cur[i]].c>0 && d[g[cur[i]].y]+1==d[i]) break;
if (cur[i]==0){
if (–num[d[i]]==0) 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){
FF+=change();
i=S;
}
}
}
return FF;
}

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(){
memset(v,0,sizeof(v));
scanf("%d%d%d",&n,&S,&T);
S+=n;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
}

void work(){
if (S-n==T || a[S-n][T]==1){
printf("NO ANSWER!n");
return;
}
e=0; memset(ls,0,sizeof(ls));
for (int i=1;i<=n;i++) addedge(i,i+n,1);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (a[i][j]) addedge(i+n,j,0x7FFFFFFF);
int flow=sap();
printf("%dn",flow);
for (int i=1;i<=n;i++){
if (flow==0) break;
v[i]=1;
e=0; memset(ls,0,sizeof(ls));
for (int j=1;j<=n;j++)
if (v[j]==0) addedge(j,j+n,1);
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
if (a[j][k]) addedge(j+n,k,0x7FFFFFFF);
int tf=sap();
if (tf else v[i]=0;
}
for (int i=1;i<=n;i++)
if (v[i]) printf("%d ",i);
}

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

[POJ 1637] 混合图的欧拉回路、网络流

【题目大意】给定一个混合图(有无向边也有有向边),问图中是否存在欧拉回路。另外该题保证存在一个顶点,使得其他顶点都能到达。即保证的连通性,我们只需要考虑度的限制就可以了。

【算法分析】对于有向边,度是不可改变的。只有无向边可以变换方向。
我们先把无向边任意地定一个方向。
1、如果有边的总度数为奇数,那么肯定不存在欧拉回路。
2、对于出度大于入度的点连边[S,i],c=-du[i]/2。du为入度-出度。
对于入度大于出度的点连边[i,T],c=du[i]/2。
然后把无向边都按你定的方向加入图中,容量都为1。
进行最大流。
如果最后与源或汇相连的边都是满流,则符合条件。
————————————————————————————————————————————
为什么要这样呢?
我们可以想象,最大流流过那条边,相当于把这条边反向,而如果要让du[i]变为0,那么必须反向du[i]/2条边。所以源汇相连的边容量为du[i]/2。然后中间的可以看成一个二分图,模拟一下就会知道是什么意思。

【其它】 6389700 edward2 1637 Accepted 420K 0MS G++ 2796B 2010-01-30 18:36:51 排得挺靠前啊。。。虽然都是0MS,ORZ

【CODE】
#include #include #include #define N 202
#define Ed 20001
struct gtp{int x,y,c,op,next;}g[Ed];
struct gtp2{int x,y,next;}G[Ed];
int n,e,m,E;
int du[N],wxn[N],ls[N],LS[N];
int fa[N],cur[N],num[N],d[N];

void addEdge(int x,int y){
E++;
G[E].x=x; G[E].y=y; G[E].next=LS[x]; LS[x]=E;
}

void init(){
E=0;
memset(du,0,sizeof(du));
memset(wxn,0,sizeof(wxn));
memset(LS,0,sizeof(LS));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
int x,y,q;
scanf("%d%d%d",&x,&y,&q);
if (q==1){
du[x]–;
du[y]++;
}
else{
addEdge(x,y);
du[x]–;
du[y]++;
}
}
}

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

void build(){
e=0;
memset(ls,0,sizeof(ls));
for (int i=1;i<=n;i++){
if (du[i]<0) addedge(0,i,-du[i]/2);
if (du[i]>0) addedge(i,n+1,du[i]/2);
for (int t=LS[i];t!=0;t=G[t].next)
addedge(G[t].x,G[t].y,1);
}
}

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

void change(){
int nf=0x7FFFFFFF;
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;
}
}

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

bool flag(){
for (int i=1;i<=n;i++)
if (du[i]%2!=0) return false;
for (int i=1;i<=n;i++)
for (int t=ls[i];t!=0;t=g[t].next)
if ((t&1) && (g[t].x==0 || g[t].y==n+1) && g[t].c!=0) return false;
return true;
}

int main(){
int tc;
scanf("%d",&tc);
for (int i=0;i init();
build();
sap();
if (flag()) printf("possiblen");
else printf("impossiblen");
}
return 0;
}