[POJ 3678]2-SAT判定

【题目大意】给定N个布尔型常量以及他们的一些运算结果,给出的运算包括and、xor、or,问能否找到一组常量的值使得它们满足这些约束?

【算法分析】2-SAT判定法。设i为false,i’为true
则对于约束这样连有向边(边[A,B]的意思是选了A,就必须选B):
i and j=1————–>add(i,i’)   add(j,j’)
i and j=0————–>add(i’,j)   add(j’,i)
i or j=1—————->add(i,j’)   add(j’,i)
i or j=0—————->add(i’,i)   add(j’,j)
i xor j=1—————>add(i’,j)   add(j’,i)  add(i,j’)  add(j,i’)
i xor j=0—————>add(i,j)    add(i’,j’) add(j,i)    add(j’,i’)
然后用对于强连通缩点,如果i和i’在同一强连通分量,那么无解。(意思是选了i又必须选i‘,选了i’也必须选i)

【其它】2WA,因为C++的字符串读入是从0开始的,ORZ。。。思想还停留在用pascal的时候。。。悲剧。。。
6398008 edward2 3678 Accepted 1432K 438MS G++ 2290B 2010-02-01 21:07:17

【CODE】
#include #include #include struct edge{int y;edge *next;};
int f[2000],dep[2000],color[2000],n,m,e;
edge *ls[2000];

inline void add(int x,int y){
edge *p=new edge;
p->y=y; p->next=ls[x]; ls[x]=p;
}

// i means i (a[i]=0)
// i+n means i’ (a[i]=1)
void init(){
char op[1000];
scanf("%d %dn",&n,&m);
for (int i=0;i for (int i=1;i<=m;i++){
int x,y,ans;
scanf("%d %d %d %sn",&x,&y,&ans,&op);
if (op[0]==’A’){
if (ans){
add(x,x+n);
add(y,y+n);
// add(x+n,y+n); add(y+n,x+n); /*need?*/
}
else{
add(x+n,y);
add(y+n,x);
}
}
if (op[0]==’O’){
if (ans){
add(x,y+n);
add(y,x+n);
}
else{
add(x+n,x);
add(y+n,y);
// add(x,y); add(y,x); /*need?*/
}
}
if (op[0]==’X’){
if (ans){
add(x,y+n);
add(x+n,y);
add(y,x+n);
add(y+n,x);
}
else{
add(x,y);
add(y,x);
add(x+n,y+n);
add(y+n,x+n);
}
}
}
}

int gf(int k){
if (f[k]==k) return k;
f[k]=gf(f[k]);
return f[k];
}

void dfs(int k){
for (edge *t=ls[k];t;t=t->next)
if (dep[t->y]==0){
dep[t->y]=dep[k]+1;
dfs(t->y);
if (dep[gf(t->y)] }
else if (color[gf(t->y)]==0 && dep[gf(t->y)] f[k]=f[t->y];
color[k]=1;
}

void tarjan(){
for (int i=0;i for (int i=0;i if (dep[i]==0){
dep[i]=1;
dfs(i);
}
for (int i=0;i}

bool print(){
for (int i=0;i if (f[i]==f[i+n]) return false;
return true;
}

int main(){
init();
tarjan();
if (print()) printf("YESn");
else printf("NOn");
return 0;
}

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