参考:http://www.cnblogs.com/scau20110726/archive/2012/12/09/2810654.html
题意:
最小费用最大流:
由于题目规定了起点和终点分别为1和n,所以我们另外设置一个源点和汇点0和n+1,0到1有一条有向边,n到n+1有一条有向边,这两条边的容量都是2,单位费用都是0
另外,题目给的边(无向边),全部拆成两条有向边,容量都是1,单位费用就是原本给的边权。然后求新源点0到新汇点n+1的最小费用最大流,如果最后最大流量为2,那么原问题成功,输出最小费用,最小费用即原问题的解。如果最大流量小于2,那么原问题失败,输出Back to jail
这个算法的正确性是什么呢?其实,原图的边,容量都设置为1,就起到了“每条边只走一次的要求”,因为每次增广后,某些边一定会满流,不会再经过,而从0出发到n+1,如果最后流量为2,那说明其实有两条路径能到达n+1,而从0到n+1,是必须经过1和n的,(别忘了我们怎么设置的那两条特殊的有向边和他们的容量)。而最小费用最大流是用费用来求解最短路,所以我们把边权设置为费用,这样一求,就等价于原问题了。
<span style="font-weight: normal;">#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<climits>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std;
const int INF=1000000000;
const int maxn=100+10;
int n,m,s,t;
struct Edge
{
int from,to,cap,flow,cost;
Edge(int from=0,int to=0,int cap=0,int flow=0,int cost=0):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
vector<Edge> edges;
vector<int> G[maxn];
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init()
{
for(int i=0;i<=n+1;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));
int v=edges.size();
G[from].push_back(v-2);
G[to].push_back(v-1);
}
bool BellmanFord(int s,int t,int &flow,int &cost)
{
for(int i=0;i<=n+1;i++)d[i]=INF;
memset(inq,0,sizeof(inq));
d[s]=0;
inq[s]=1;
p[s]=0;
a[s]=INF;
queue<int> 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;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
}
void solve(int s,int t)
{
int flow=0,cost=0;
while(BellmanFord(s,t,flow,cost));
if(flow<2)printf("Back to jail\n");
else printf("%d\n",cost);
}
int main()
{
while(~scanf("%d",&n))
{
if(n==0)break;
scanf("%d",&m);
s=0;
t=n+1;
init();
for(int i=0;i<m;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
AddEdge(u,v,1,c);
AddEdge(v,u,1,c);
}
AddEdge(0,1,2,0);
AddEdge(n,n+1,2,0);
solve(s,t);
}
return 0;
}</span>
最短路:
把无向图当做有向图处理,拆成两条边,先从1到n做一次最短路,并且要记录这条有向路径。最短路结束后,记录下最短路的值d[t],如果为d[t]=INF(t=n)说明图都不连通,从起点娶不到终点。
如果连通能去到,那么把这条路径上的边的值都取相反数,并且删除它的相反边,即赋值为INF
即 u--->v 是去的时候路径的其中有向边,那么g[u][v]=-g[u][v]; g[v][u]=INF;
然后从n到1再运行一遍最短路,因为这次有些边是负值,所以dij不能用,可以用spfa,所以我的代码两次都是spfa,写一个就行了,有些人是先写一个dij再写一个spfa也可以的
如果这次最短路d[t]=INF(此时的t=1),说明回不去了,那么失败
很多人见到题目,就是直接1到n一次最短路,n到1一次最短路,再求和,但是这样是错的,我也是这样做,WA
那么这个算法的正确性是什么呢
如果从1到n有两(多)条最短路,并且有些最短路没有共用边,即完全分离的,那么去的时候用 一条,回的时候用一条,互不干扰,最后的和就是最短路*2
同样的,如果只有1条最短路和次短路(1条和多条都无所谓),而且他们没有共用边,那么去的时候用最短路,回的时候用次短路,互不干扰。最后的和是最短路+次短路
但是,不幸的是,可能有两次最短路,但是却有共用边,那么去的时候肯定会用掉这条共用边,因为回来的时候不能再用这条共用边,那么是不是应该完全放弃另一条没有用过的最短路而另寻路径呢?不是的,而是可以用一个最好的方法,就是消去那条共用边的权(想想为什么来时候的边权要取反)
可以这样理解,两条最短路,都可以看成 前部分+共用边+后部分 , 这里前部分和后部分都是独立的,没有共用的,那么综合两次的走法,其实可以变为 最短路1的前部分+最短路2的后部分,为去的路径,最短路2的后部分+最短路1的前部分,为回的路径,这样子,相当于交换了路径,但是我们并不关心路径,我们只关心两次和最小,这样并不改变和,而且还消掉了共用边的权,其实相当于两次走都没有进过共用边
所以这样的和为 最短路*2-所有共用边的权
同样,所过最短路和次短路有共用边,那么同样是相当于交叉了两次的路径,但是并不改变权和,而且消去了共用边的权
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<climits>
#include<map>
#include<string>
using namespace std;
const int INF=1000000000;
const int maxn=100+10;
int n,m;
int d[maxn];
int G[maxn][maxn];
int p[maxn];
void change(int num)
{
if(num==1)return ;
int u=p[num];
G[u][num]=-G[u][num];
G[num][u]=INF;
change(u);
}
void Bellman_Ford(int s)
{
queue<int> q;
bool inq[maxn];
for(int i=1;i<=n;i++)
{
d[i]=(i==s ? 0:INF);
p[i]=1;
}
memset(inq,0,sizeof(inq));
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
inq[x]=false;
for(int i=1;i<=n;i++)if(d[i]>d[x]+G[x][i])
{
d[i]=d[x]+G[x][i];
p[i]=x;
if(!inq[i])
{
inq[i]=true;
q.push(i);
}
}
}
}
int main()
{
while(~scanf("%d",&n))
{
if(n==0)break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)G[i][j]=0;
else G[i][j]=INF;
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
G[u][v]=c;
G[v][u]=c;
}
Bellman_Ford(1);
int res=0;
if(d[n]==INF)printf("Back to jail\n");
else
{
res+=d[n];
change(n);
Bellman_Ford(n);
if(d[1]==INF)printf("Back to jail\n");
else {res+=d[1];printf("%d\n",res);}
}
}
return 0;
}