这题是网络流最大流。
如果有两点之间是危桥,则连一条流量为2的边,不然流量为∞。然后再取一点S(权且称为0号点)为源点与a1,b1连一条流量为∞的边,点T(权且称为N+1号点)为汇点与a2,b2连一条流量为∞的边,跑一边最大流(Dinic或EK等等)。
但这并未结束,可能出现a1流到b2,但b1未流到的情况。所以换一下b1,b2的位置(即S向b2连边,b1向T连边),再跑一遍最大流,两次都符合条件的则输出Yes,不然输No即可。
晒一下代码(Dinic算法):
/**************************************************************
Problem: 3504
User: fantasticwtl
Language: C++
Result: 正确
Time:48 ms
Memory:884 kb
****************************************************************/
#include <cstdio>
#include <cmath>
#include <memory.h>
#include <algorithm>
using namespace std;
int d[101],g[101][101],q[101],gg[101][101],n,a1,a2,b1,b2,an,bn;
char c,enter;
bool jz ()
{
memset(d,0,sizeof(d));
d[0]=1;q[1]=0;
int h=0,t=1;
while(h<t){
h++;
for(int i=0;i<=n;i++)
if(d[i]==0&&g[q[h]][i]){
t++;d[i]=d[q[h]]+1;q[t]=i;
}
if(d[n])return 1;
}
if(d[n]==0)return 0;else return 1;
}
int find (int x,int y)
{
if(x>=n)return y;
int a=0;
for(int i=0;i<=n;i++)
if(d[i]==d[x]+1&&g[x][i]>0&&(a=find(i,min(y,g[x][i])))){g[x][i]-=a;g[i][x]+=a;return a;}
return 0;
}
int main ()
{
while(scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF){
bool ff=0;
memset(g,0,sizeof(g));memset(gg,0,sizeof(gg));
a1++;a2++;b1++;b2++;
for(int i=1;i<=n;i++){
scanf("%c",&enter);//处理换行
for(int j=1;j<=n;j++){
scanf("%c",&c);
if(i>j){
if(c=='O')g[j][i]=gg[j][i]=g[i][j]=gg[i][j]=2;//危桥
if(c=='N')g[j][i]=gg[j][i]=g[i][j]=gg[i][j]=100000000;//非危桥
}
}
}//读入部分
n++;
g[0][a1]=2*an+1;g[0][b1]=2*bn+1;g[a2][n]=2*an+1;g[b2][n]=2*bn+1;
int q=0;
while(jz ())q+=find (0,1000000000);//第一次Dinic
if(q<2*(an+bn))ff=1;
if(!ff){//如果满足第一次才做第二次
memset(g,0,sizeof(g));
for(int i=1;i<=n-1;i++)
for(int j=1;j<=n-1;j++)g[i][j]=gg[i][j];
g[0][a1]=2*an+1;g[0][b2]=2*bn+1;g[a2][n]=2*an+1;g[b1][n]=2*bn+1;
int q=0,w=0;
while(jz())if(w=(find(0,100000000)))q+=w;//第二次Dinic
if(q<2*(an+bn))ff=1;
}
if(ff)printf("No\n");else printf("Yes\n");
}
}
多消化消化最大流,明天开始做最小费用最大流。