[CQOI2014]危桥 指定的2个起点和2个终点的网络流
题目
([CQOI2014]危桥)https://ac.nowcoder.com/acm/problem/19931
题目描述
Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1。某些岛屿之间有桥相连,桥上的道路是双向的,但一次只能供一人通行。其中一些桥由于年久失修成为危桥,最多只能通行两次。Alice希望在岛屿a1和a2之间往返an次(从a1到a2再从a2到a1算一次往返)。同时,Bob希望在岛屿b1和b2之间往返bn次。这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行。请问Alice和Bob能完成他们的愿望吗?
输入描述:
本题有多组测试数据。 每组数据第一行包含7个空格隔开的整数,分别为N、a1、a2、an、b1、b2、bn。
接下来是一个N行N列的对称矩阵,由大写字母组成。矩阵的i行j列描述编号i一1和j-l的岛屿间的连接情况,若为“O”则表示有危桥相连;为“N”表示有普通的桥相连;为“X”表示没有桥相连。
输出描述:
对于每组测试数据输出一行,如果他们都能完成愿望输出“Yes”,否则输出“No”。
示例1
输入
4 0 1 1 2 3 1
XOXX
OXOX
XOXO
XXOX
4 0 2 1 1 3 2
XNXO
NXOX
XOXO
OXOX
输出
Yes
No
说明
数据范围
4<=N<50
O<=a1, a2, b1, b2<=N-1
1 <=an,bn<=50
思路
因为经过次数是来回,所以我们得到一个很简单的建模:
S向a1, b1分别连一条容量为an, bn的边。
a2, b2分别向T连一条an, bn的边。
危桥之间连接容量为1的边。普通边连接inf的边。
但是一个问题。怎么确保a1的流量全部是流向a2的。
如下图:
u-v是所有a1-b2和b1-a2的链之间的流量。这样也可以得到最大流但是并不满足题目要求。除非u->v的考研流x+y的流量。
那么我们怎么确定u->v能不能留x+y的流量。我们交换有b1和b2的位置。如果还能最大流,所有的流量一定经过u-v这条边。
那么就说明满足条件了。
而且如果之前本来就是a1->a2,b1->b2那么交换b1, b2并不会影响结果。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<iostream>
#define INF 1e9
using namespace std;
const int maxn =1000+10;
struct Edge {
int from,to,cap,flow;
Edge() {}
Edge(int f,int t,int c,int fl):from(f),to(t),cap(c),flow(fl) {}
};
struct Dinic {
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
int cur[maxn];
int d[maxn];
bool vis[maxn];
void init(int n,int s,int t) {
this->n=n, this->s=s, this->t=t;
edges.clear();
for(int i=0; i<n; i++)
G[i].clear();
}
void AddEdge(int from,int to,int cap) {
edges.push_back( Edge(from,to,cap,0) );
edges.push_back( Edge(to,from,0,0) );
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS() {
queue<int> Q;
Q.push(s);
memset(vis,0,sizeof(vis));
d[s]=0;
vis[s]=true;
while(!Q.empty()) {
int x=Q.front();
Q.pop();
for(int i=0; i<G[x].size(); ++i) {
Edge& e=edges[G[x][i]];
if(!vis[e.to] && e.cap>e.flow) {
d[e.to]=1+d[x];
vis[e.to]=true;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a) {
if(x==t || a==0)
return a;
int flow=0,f;
for(int& i=cur[x]; i<G[x].size(); ++i) {
Edge& e=edges[G[x][i]];
if(d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow) ) )>0) {
e.flow +=f;
edges[G[x][i]^1].flow -=f;
flow +=f;
a-=f;
if(a==0)
break;
}
}
return flow;
}
int max_flow() {
int ans=0;
while(BFS()) {
memset(cur,0,sizeof(cur));
ans += DFS(s,INF);
}
return ans;
}
} DC;
char s[55][55];
int main() {
int n, a1, a2, an, b1, b2, bn;
while(~scanf("%d%d%d%d%d%d%d", &n, &a1, &a2, &an, &b1, &b2, &bn)) {
a1++, a2++, b1++, b2++;
int S=0, T=n+1;
DC.init(n+5, S, T);
for(int i=1; i<=n; i++) {
scanf("%s", s[i]+1);
for(int k=1; k<=n; k++) {
if(s[i][k]=='O') {
DC.AddEdge(i, k, 1);
}
if(s[i][k]=='N') {
DC.AddEdge(i, k, INF);
}
}
}
DC.AddEdge(S, a1, an);
DC.AddEdge(S, b1, bn);
DC.AddEdge(a2, T, an);
DC.AddEdge(b2, T, bn);
if(DC.max_flow()!=an+bn) {
printf("No\n");
continue;
}
DC.init(n+5, S, T);
for(int i=1; i<=n; i++) {
for(int k=1; k<=n; k++) {
if(s[i][k]=='O') {
DC.AddEdge(i, k, 1);
}
if(s[i][k]=='N') {
DC.AddEdge(i, k, INF);
}
}
}
DC.AddEdge(S, a1, an);
DC.AddEdge(S, b2, bn);
DC.AddEdge(a2, T, an);
DC.AddEdge(b1, T, bn);
if(DC.max_flow()!=an+bn) {
printf("No\n");
continue;
}
printf("Yes\n");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)