题意:给你一个n个点 m条边的有向图 问你是否存在一个从1到n且不小于c的流 如果不存在能否通过改变一条边的容量达到要求
思路:这个题就好像最短路树一样 只要改变的路不是最短路树上的 最短距离就不会改变 这个题是只有改变最小割边的容量 最大流才会改变 所以只要求出最小割即可
本题有两个重要的优化 详见代码
PS 为什么我的代码比大神们的多辣么多 QAQ
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define REP( i, a, b ) for( int i = a; i < b; i++ )
#define FOR( i, a, b ) for( int i = a; i <= b; i++ )
#define CLR( a, x ) memset( a, x, sizeof a )
#define CPY( a, x ) memcpy( a, x, sizeof a )
const int maxn = 100 + 10;
const int maxe = 20000 + 10;
const int INF = 1e9;
struct Edge{
int u, v, c, f;
int next;
Edge() {}
Edge(int u, int v, int c, int f, int next) : u(u), v(v), c(c), f(f), next(next) {}
bool operator < (const Edge &rhs) const{
if(u != rhs.u) return u < rhs.u;
return v < rhs.v;
}
};
struct ISAP{
int n, s, t;
int num[maxn], cur[maxn], d[maxn], p[maxn];
int Head[maxn], cntE;
int Q[maxn], head, tail;
int cut[maxe], cutE, vis[maxn];
Edge edge[maxe];
void Init(int n){
this -> n = n;
cntE = cutE = 0;
CLR(Head, -1);
CLR(vis, 0);
}
void Add(int u, int v, int c){
edge[cntE] = Edge(u, v, c, 0, Head[u]);
Head[u] = cntE++;
edge[cntE] = Edge(v, u, 0, 0, Head[v]);
Head[v] = cntE++;
}
void Bfs(){
CLR(d, -1);
CLR(num, 0);
d[t] = 0;
head = tail = 0;
Q[tail++] = t;
num[0] = 1;
while(head != tail){
int u = Q[head++];
for(int i = Head[u]; ~i; i = edge[i].next){
Edge &e = edge[i];
if(~d[e.v]) continue;
d[e.v] = d[u] + 1;
Q[tail++] = e.v;
num[d[e.v]] ++;
}
}
}
int Maxflow(int s, int t, int maxflow){
this -> s = s;
this -> t = t;
CPY(cur, Head);
Bfs();
int flow = 0, u = p[s] = s;
while(d[s] < n){
if(u == t){
int f = INF, neck;
for(int i = s; i != t; i = edge[cur[i]].v){
if(f > edge[cur[i]].c - edge[cur[i]].f){
f = edge[cur[i]].c - edge[cur[i]].f;
neck = i;
}
}
for(int i = s; i != t; i = edge[cur[i]].v){
edge[cur[i]].f += f;
edge[cur[i]^1].f -= f;
}
flow += f;
if(flow > maxflow) return flow;//优化2:不用增广到底 只要当前最大流量超过题目限定就可以直接退出
u = neck;
}
int ok = 0;
for(int i = cur[u]; ~i; i = edge[i].next){
Edge &e = edge[i];
if(e.c > e.f && d[e.v] + 1 == d[u]){
ok = 1;
cur[u] = i;
p[e.v] = u;
u = e.v;
break;
}
}
if(!ok){
int m = n - 1;
if(--num[d[u]] == 0) break;
for(int i = Head[u]; ~i; i = edge[i].next){
Edge &e = edge[i];
if(e.c - e.f > 0 && m > d[e.v]){
cur[u] = i;
m = d[e.v];
}
}
++num[d[u] = m + 1];
u = p[u];
}
}
return flow;
}
void Reduce(){
REP(u, 0, n) for(int i = Head[u]; ~i; i = edge[i].next){
edge[i].c -= edge[i].f;
}
}
void Clearflow(){
REP(u, 0, n) for(int i = Head[u]; ~i; i = edge[i].next){
edge[i].f = 0;
}
}
void Mincut(int x){
vis[x] = 1;
for(int i = Head[x]; ~i; i = edge[i].next){
if(edge[i].c > edge[i].f && !vis[edge[i].v])
Mincut(edge[i].v);
}
}
void Findcut(){
Mincut(1);
REP(u, 0, n) for(int i = Head[u]; ~i; i = edge[i].next){
Edge &e = edge[i];
if(vis[e.u] && !vis[e.v] && e.c > 0) cut[cutE++] = i;//保存割边
}
}
}solver;
int N, M, C, cas = 0;
void input(){
int u, v, c;
solver.Init(N+1);
REP(i, 0, M){
scanf("%d%d%d", &u, &v, &c);
solver.Add(u, v, c);
}
}
void solve(){
printf("Case %d: ", ++cas);
int flow = solver.Maxflow(1, N, C);
if(flow >= C) printf("possible\n");
else{
vector<Edge> ans;
solver.Findcut();
solver.Reduce();//优化1:不用每次都重新求最大流 只需在原来的基础之上增广即可
REP(i, 0, solver.cutE){
solver.Clearflow();
int e = solver.cut[i];
solver.edge[e].c = C;
int F = solver.Maxflow(1, N, C - flow);
if(F + flow >= C) ans.push_back(solver.edge[e]);
solver.edge[e].c = 0;
}
if(!ans.size()) printf("not possible\n");
else{
sort(ans.begin(), ans.end());
printf("possible option:(%d,%d)", ans[0].u, ans[0].v);
REP(i, 1, ans.size()) printf(",(%d,%d)", ans[i].u, ans[i].v);
printf("\n");
}
}
}
int main()
{
//freopen("in.txt", "r", stdin);
while(scanf("%d%d%d", &N, &M, &C) && N){
input();
solve();
}
return 0;
}
本文介绍了一个基于ISAP算法解决特定图论问题的方法,该问题旨在判断是否存在从节点1到节点n且流量不小于c的流,若不存在,则考虑通过调整单条边的容量来满足条件。文章详细解释了算法原理,并提供了高效的实现方案。
265

被折叠的 条评论
为什么被折叠?



