解题报告 之 SGU185 Two shortest
Description
Input
Output
Sample test(s)
Input
1 2 4 6
题目大意:给一个无向图,有n个点,m条边。起点为1,终点为n。然后问你是否存在两条最短路,其边不重复。如果存在则打印这两条最短路。不存在则输出"No solution"。
分析:题意相对简单,但是卡内存太坑了。不想交最短路模型请见另一篇博文。
ZOJ2760 How Many Shortest Path
http://blog.youkuaiyun.com/maxichu/article/details/45151399
首先考虑一般做法,先Floyd跑最短路,让后将所有最短路中的边加入流量图,跑最大流看看有几条最短路。但是这道题卡内存就造成了如果用Floyd的话内存会不够。所以我们必须换,首先想到SPFA,这样就可以让数组dist降维。根据最短路的子路也一定是最短路这一理论,我们遍历i、j,让所有满足添加边,负载为1。
if(dist[i] + map[i][j] == dist[j])
{
addedge( i, j, 1 );
}
然后跑最大流,如果>=2在说明有两条最短路。如果找得到两条最短路,则再dfs搜两次,将满足cap为0且不是逆向边的边打印,然后继续dfs,即可打印出路径。注意在第一次dfs时,如果打印了某边,则将cap设为-1,防止第二次dfs又搜到。还有一个问题是两条最短路中可能存在一些路径是可以互换的,但无论怎么换,深搜一定可以正确找到两条最短路,想想为什么?
上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 410;
const int MAXM = 260100;
const int INF = 0x3f3f3f3f;
struct Edge
{
int to, next, cap;
};
Edge edge[MAXM];
int level[MAXN];
int head[MAXN];
int dist[MAXN];
int map[MAXN][MAXN];
int src, des, cnt, flag;
void SPFA(int n)
{
int inqueue[MAXN];
memset( dist, INF, sizeof dist );
memset( inqueue, 0, sizeof inqueue );
dist[1] = 0;
deque<int> dq;
dq.push_back( 1 );
inqueue[1] = 1;
while(!dq.empty())
{
int next = dq.front();
dq.pop_front();
inqueue[next] = 0;
for(int i = 1; i <= n; i++)
{
if(dist[i] > dist[next] + map[next][i])
{
dist[i] = dist[next] + map[next][i];
if(!inqueue[i])
{
if(!dq.empty() && dist[i] > dist[dq.front()])
dq.push_front( i );
else
dq.push_back( i );
}
}
}
}
}
void addedge( int from, int to, int cap )
{
edge[cnt].to = to;
edge[cnt].cap = cap;
edge[cnt].next = head[from];
head[from] = cnt++;
swap( from, to );
edge[cnt].to = to;
edge[cnt].cap = 0;
edge[cnt].next = head[from];
head[from] = cnt++;
}
int bfs()
{
memset( level, -1, sizeof level );
cnt = 0;
queue<int> q;
while (!q.empty())
q.pop();
level[src] = 0;
q.push( src );
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].cap > 0 && level[v] == -1)
{
level[v] = level[u] + 1;
q.push( v );
}
}
}
return level[des] != -1;
}
int dfs( int u, int f )
{
if (u == des) return f;
int tem;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].cap > 0 && level[v] == level[u] + 1)
{
tem = dfs( v, min( f, edge[i].cap ) );
if (tem > 0)
{
edge[i].cap -= tem;
edge[i^1].cap += tem;
return tem;
}
}
}
level[u] = -1;
return 0;
}
int Dinic()
{
int ans = 0, tem;
while (bfs())
{
while ((tem = dfs( src, INF )) > 0)
{
ans += tem;
}
}
return ans;
}
void print( int n, int u )
{
if(u != n)
{
printf( "%d ", u );
}
else
{
printf( "%d\n", u );
flag = true;
return;
}
for(int i = head[u]; i != -1&&!flag; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap == 0 && i % 2 == 0)
{
edge[i].cap = -1;
print( n, v );
}
}
}
int main()
{
int n,m;
while(cin >> n >> m)
{
src = 1, des = n;
memset( head, -1, sizeof head );
memset( map, INF, sizeof map );
for(int i = 1; i <= n; i++)
map[i][i] = 0;
cnt = 0;
int a, b, c;
for(int i = 1; i <= m; i++)
{
cin >> a >> b >> c;
map[a][b] = map[b][a] = c;
}
SPFA( n );
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(dist[i] + map[i][j] == dist[j])
{
addedge( i, j, 1 );
}
}
}
int ans = Dinic();
if(ans < 2)
cout << "No solution" << endl;
else
{
print( n,src );
flag = false;
print( n, src );
}
}
return 0;
}
最大流刷题暂告一段落,回归集训!

本文介绍了解决SGU185Twoshortest问题的方法,该问题要求找出从起点到终点的两条不重复边的最短路径。通过使用SPFA算法寻找最短路径,并借助最大流算法判断是否至少存在两条这样的路径。
434

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



