题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1747
题目大意:
从1点到N点走2次,两次的路径要求不能经过任意一条重边,求总代价最小,若不能则输出”Back to jail“。
解题:
很容易以为是最短路,走两遍最短路,第一遍过程中记录下最短路上的边,删去。网上很多题解都说明显是错的,但似乎没有给出证明。个人认为应该是,当同时存在两条以上最短路时,任选一条,会造成错误,若最短路唯一,这样做应该是对的。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define inf 0x3f3f3f3f
using namespace std;
int mapp[105][105],cnt;
int dist[105],path[105];
bool vis[105];
struct edge
{
int aft,bef;
}store[10010];
void init(int n)
{
cnt=0;
memset(vis,0,sizeof(vis));
memset(mapp,inf,sizeof(mapp));
memset(dist,inf,sizeof(dist));
memset(path,0,sizeof(path));
for(int i=1;i<=n;i++)
mapp[i][i]=0;
}
void dijkstra(int s,int n)
{
int tmp,minn,p;
vis[s]=1;
dist[s]=0;
for(int i=1;i<=n;i++)
{
tmp=mapp[s][i];
if(s!=i&&(tmp<inf))
{
cnt++;
store[cnt].aft=i;
store[cnt].bef=s;
path[i]=cnt;
dist[i]=tmp;
}
}
while(1)
{
minn=inf;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dist[i]<minn)
{
minn=dist[i];
p=i;
}
}
if(minn==inf)break;
vis[p]=1;
for(int i=1;i<=n;i++)
{
tmp=dist[p]+mapp[p][i];
if(tmp<dist[i])
{
dist[i]=tmp;
cnt++;
store[cnt].bef=p;
store[cnt].aft=i;
path[i]=cnt;
}
}
}
}
void update(int n)
{
int bef,x,a,b,v;
bef=n;
while(1)
{
x=path[bef];
a=store[x].aft;
b=store[x].bef;
mapp[a][b]=inf;
mapp[b][a]=inf;
bef=b;
if(bef==1)break;
}
}
int main()
{
int n,m,a,b,v,ans1,ans;
while(scanf("%d",&n))
{
if(n==0)break;
scanf("%d",&m);
init(n);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&v);
mapp[a][b]=mapp[b][a]=v;
}
dijkstra(1,n);
if(dist[n]==inf)
printf("Back to jail\n");
else
{
update(n);
ans1=dist[n];
cnt=0;
memset(vis,0,sizeof(vis));
memset(dist,inf,sizeof(dist));
memset(path,0,sizeof(path));
for(int i=1;i<=n;i++)
mapp[i][i]=0;
dijkstra(1,n);
if(dist[n]==inf)
printf("Back to jail\n");
else
{
ans=dist[n]+ans1;
printf("%d\n",ans);
}
}
}
return 0;
}
正解:
其实是非常标准的费用流,源点到1为容量为2,代价为0,,N到汇点容量为2,代价为0。然后每条边容量为1,代价即为其代价,跑一遍费用流即可。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
int sumFlow;
const int MAXN = 105;
const int MAXM = 1000200;
const int INF = 1000000000;
struct Edge
{
int u;
int v;
int cap;
int cost;
int next;
}edge[MAXM<<2];
int NE;
int head[MAXN], dist[MAXN], pp[MAXN];
bool vis[MAXN];
void init()
{
NE=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
edge[NE].u=u;edge[NE].v=v;edge[NE].cap=cap;edge[NE].cost=cost;
edge[NE].next=head[u];head[u]=NE++;
edge[NE].u=v;edge[NE].v=u;edge[NE].cap=0;edge[NE].cost=-cost;
edge[NE].next=head[v];head[v]=NE++;
}
bool SPFA(int s,int t,int n)
{
int i,u,v;
queue<int>qu;
memset(vis,false,sizeof(vis));
memset(pp,-1,sizeof(pp));
for(i=0;i<=n;i++)
dist[i]=INF;
vis[s]=true;
dist[s]=0;
qu.push(s);
while(!qu.empty())
{
u=qu.front();
qu.pop();
vis[u]=false;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].cap&&dist[v]>dist[u]+edge[i].cost)
{
dist[v]=dist[u]+edge[i].cost;
pp[v]=i;
if(!vis[v])
{
qu.push(v);
vis[v]=true;
}
}
}
}
if(dist[t]==INF)
return false;
return true;
}
int MCMF(int s,int t,int n)
{
int flow=0; // 总流量
int i,minflow,mincost;
mincost=0;
while(SPFA(s,t,n))
{
minflow=INF+1;
for(i=pp[t];i!=-1;i=pp[edge[i].u])
if(edge[i].cap<minflow)
minflow=edge[i].cap;
flow+=minflow;
for(i=pp[t];i!=-1;i=pp[edge[i].u])
{
edge[i].cap-=minflow;
edge[i^1].cap+=minflow;
}
mincost+=dist[t]*minflow;
}
sumFlow=flow; // 最大流
if(sumFlow<2)
return -1;
else
return mincost;
}
int main()
{
int n,m;
int u,v,c;
while(scanf("%d",&n))
{
if(n==0)break;
scanf("%d",&m);
init();
int S=0;
int T=n+1;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&c);
addedge(u,v,1,c);
addedge(v,u,1,c);
}
addedge(S,1,2,0);
addedge(n,T,2,0);
int ans=MCMF(S,T,T+1);
if(ans==-1)
printf("Back to jail\n");
else
printf("%d\n",ans);
}
return 0;
}