题解 P1948 【[USACO08JAN]电话线Telephone Lines】
本题有两种做法,二分+bfs和分层图最短路
二分的已经很多人讲了,这里主要讲分层图最短路。
分层图就是把图中节点分开,变成许多不同的状态。
本题可以仿照动态规划的思想,用D[x,p]表示1号到x号图中已经指定了p条免费电缆是,最贵的经过的电缆最小是多少。若有一条从x到y长度为z的无向边,应该用max(D[x,p],z)更新D[y,p]的最小值,用D[x,p]更新D[y,p+1]的最小值。
很显然这种做法是有有效性的,但是,我们可以用spfa或dij来跑一遍,使得D数组不能更新。这样也解决了后效性。用spfa来做时间复杂度为 O(tNP),其中t为常数。这道题没有卡spfa。
代码
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1001;
const int M = 20001;
int n,m,k;
struct EDGE{
int next;
int to;
int len;
}edge[M];
struct NODE{
int pos;
int free;
};
queue <NODE> q;
int head[N];
bool vis[N][N];
int cnt = 0;
int dist[N][N];
void add_edge(int x,int y,int len)
{
edge[++cnt].to = y;
edge[cnt].len = len;
edge[cnt].next = head[x];
head[x] = cnt;
}
void spfa()
{
memset(dist,0x3f,sizeof dist);
q.push((NODE){1,0});
vis[1][0] = 1;
dist[1][0] = 0;
while(!q.empty())
{
NODE top = q.front();
q.pop();
vis[top.pos][top.free] = 0;
for(int i=head[top.pos];i;i=edge[i].next)
{
int y = edge[i].to;
if(max(dist[top.pos][top.free],edge[i].len) < dist[y][top.free])
{
dist[y][top.free] = max(dist[top.pos][top.free],edge[i].len);
if(!vis[y][top.free])
{
vis[y][top.free] = 1;
q.push((NODE){y,top.free});
}
}
if(top.free < k && dist[y][top.free+1] > dist[top.pos][top.free])
{
dist[y][top.free+1] = dist[top.pos][top.free];
if(!vis[y][top.free+1])
{
q.push((NODE){y,top.free+1});
vis[y][top.free+1] = 1;
}
}
}
}
}
// dist[next][top.free] --> max(dist[top.pos][top.free] , edge[i].len
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
int x,y,l;
scanf("%d%d%d",&x,&y,&l);
add_edge(x,y,l); //无向图加条边
add_edge(y,x,l);
}
spfa();
if(dist[n][k] == 0x3f3f3f3f)
printf("-1\n");
else
printf("%d\n",dist[n][k]);
}
以上内容部分借鉴与算法竞赛进阶指南。