https://uva.onlinejudge.org/external/108/10806.pdf
终于可以写这道题的题解了,昨天下午纠结我一下下午,晚上才照着人家的题解敲出来,今天上午又干坐着想了两个小时,才弄明白这个问题。
题意很简单,给出一个无向图,要求从1 到 n最短路两次,但是两次不允许经过同一条边(正反都不能经过),如果能成功,则输出两次的最小长度。否则输出一句话。
我当时就马上敲了一个最短路,执行两次,在第一次执行完之后就把所经过的路径的正反都锁定好,不允许下次再访问。。。这样做通过了sample,但是WA了,我后来上网查,原来好多人是我这种做法,。。而且原来这是最小费用最大流问题。
分析一下为什么我的做法不行,因为这道题目要求在能通过的前提下,最短,也就是说,我连续两次最短路,如果第二次不能通过,就放弃,这明显是错的,最终的结果,也许是一条最短路+一条长路,或者两条都不是最短路。。。
所以一种可行的修改方案是,在原来的代码基础上,不是直接封锁正反两条路,而是封锁正向,把反向边=权值的相反数,为什么这样的,简而言之,就是给出反悔的机会
我们来思考这样一种实际的例子 如果 path1:起点-p1-A-B-p2-终点; path2:起点-p3-B-A-P4-终点;p1 p2 p3 p4都是互不干扰的路径,这样的话,其实两条路径就是p1-p3
p2-p4,A-B作为公共边,在程序里面最短路确实是这样通过的,但实际上由于权值取反,正好抵消,使得A-B B-A实际意义上是没有被经过,但是路径是通的,这样就能够智能的去选择路径方案。
最终我是用最小费用最大流来解决的,通过这个题目,我对最大流有了更深刻的理解,明白了残量图的更大的作用,为什么当时E-K算法里面,找到增广路后需要对正向流进行扩展,同时又对反向流减少,这其实就是对应了现实的流的概念,我递增正向,但是流是守恒的,我可以通过反向把流又送回来,因此用这个方法,使得最大流不会放过任何一个能走通的路径,在E-K算法里,其实流经过了多次往返,只有确定无法走通,才会放弃。。。同时在建图的时候,因为是无向图,所以每条边,都要建立一个相应的负费用边。。。这样的话每个端点实际上连了两条边,一个是自己的正费用边,一个是对面端点的负费用边,我一开始会觉得这样在最短路过程中会不会发生混乱,即这个点往下走,到底是走的自己本身的正费用边,还是负费用边,后来不管费用如何,最短路最终会选择最优的那条,因此不会有问题
一条无向边对应2条有向的 每条还有一条相反的 cap = 0 cost = -w的反向边 总共4条
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 2000 + 10;
const int INF = 1000000;
struct Edge {
int from, to, cap, flow, cost;
Edge(int u, int v, int c, int f, int w):from(u),to(v),cap(c),flow(f),cost(w) {}
};
struct MCMF {
int n, m;
vector edges;
vector G[maxn];
int inq[maxn]; // 是否在队列中
int d[maxn]; // Bellman-Ford
int p[maxn]; // 上一条弧
int a[maxn]; // 可改进量
void init(int n) {
this->n = n;
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap, int cost) {
edges.push_back(Edge(from, to, cap, 0, cost));
edges.push_back(Edge(to, from, 0, 0, -cost));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s, int t, int flow_limit, int& flow, int& cost) {
for(int i = 0; i < n; i++) d[i] = INF;
memset(inq, 0, sizeof(inq));
d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
queue Q;
Q.push(s);
while(!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
d[e.to] = d[u] + e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap - e.flow);
if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
}
}
}
if(d[t] == INF) return false;
if(flow + a[t] > flow_limit) a[t] = flow_limit - flow;
flow += a[t];
cost += d[t] * a[t];
for(int u = t; u != s; u = edges[p[u]].from) {
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
return true;
}
// 需要保证初始网络中没有负权圈
int MincostFlow(int s, int t, int flow_limit, int& cost) {
int flow = 0; cost = 0;
while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost));
return flow;
}
};
MCMF mcmf;
int main() {
int n,m,icount=1;
int s,t;
while(scanf("%d", &n) == 1 && n)
{
mcmf.init(n+1);
s=0;
t=n;
mcmf.AddEdge(0, 1, 2, 0);
scanf("%d", &m);
for(int i=0;i