一句话题意:
一个图中,对于每一条边有两个权值:路费和时间,我们要找一条从源点出发到终点的路,在这条路的时间不超过给定时间的基础上,找到路费最少的路。
分析:
如果按照普通的最短路,会因为有一个时间的限制而找不到正确的解。
找不到正确的解的原因就是对于每个点只能存一个时间,然而我们可能在多个不同时间到达一个点,不一定哪个时间会得到答案,后来的可能会把原来的覆盖掉,覆盖掉的就可能是正确的解。
按照这个想法,我们可以对于每一个点,保存每一个时间到达的情况。也就是存花费的数组变成一个二维数组。
第一维表示第几个点,第二维表示在什么时间到达这个点。
cost[i][j] = x的意思为:在时间 j 到达点 i 的最小化费为 x 。
剩下的就是基本的最短路了。
另外,因为本题数据量略小,DFS暴力也能出来,但是需要剪一下枝。
下面附上两种做法的代码
AC代码:
- 二维最短路
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxv = 100;
int cnt, vertex, time_limit;
int cost[maxv][1010], map_cost[maxv][maxv], map_time[maxv][maxv];
bool vis[maxv][1010];
void spfa()
{
queue <pair <int, int> > que;
que.push(make_pair(0, 0));
cost[0][0] = 0;
while (!que.empty())
{
int cur_pos = que.front().first;
int cur_time = que.front().second;
que.pop();
vis[cur_pos][cur_time] = false;
for (int i = 0; i < vertex; i++)
{
if (cur_pos == i)
continue;
int to_time = cur_time + map_time[cur_pos][i];
if (to_time > time_limit)//如果时间已经超出,则不需要往下继续跑
continue;
if (cost[cur_pos][cur_time] + map_cost[cur_pos][i] < cost[i][to_time])//最短路的松弛操作
{
//注意是从当前节点的当前时间到目的节点的目的时间
cost[i][to_time] = cost[cur_pos][cur_time] + map_cost[cur_pos][i];
if (!vis[i][to_time])
{
que.push(make_pair(i, to_time));
vis[i][to_time] = true;
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> vertex >> time_limit && (vertex || time_limit))
{
memset(vis, 0, sizeof(vis));
memset(cost, inf, sizeof(cost));
for (int i = 0; i < vertex; i++)
for (int j = 0; j < vertex; j++)
cin >> map_time[i][j];
for (int i = 0; i < vertex; i++)
for (int j = 0; j < vertex; j++)
cin >> map_cost[i][j];
spfa();
int ans_time, ans_cost = inf;
//遍历目的节点的每一个时间,找到所有的时间里最少的花费并记录下来
for (int i = 0; i <= 1000; i++)
{
if (ans_cost > cost[vertex - 1][i])
{
ans_cost = cost[vertex - 1][i];
ans_time = i;
}
}
cout << ans_cost << ' ' << ans_time << endl;
}
return 0;
}
- DFS
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int toll[100][100], Time[100][100], vis[100];
int vertex, latestTime, ansToll = inf, ansTime = inf;
void DFS(int cur, int dis, int time)
{
if (cur == vertex)//到达了节点,保存一下最小花费
{
if (dis < ansToll)
{
ansTime = time;
ansToll = dis;
}
return;
}
for (int i = 1; i <= vertex; i++)
{
//注意这里需要加一个 "dis + toll[cur][i] <= ansToll"的剪枝
//如果当前的花费已经超过了已经求出的答案的最小花费,那么没有必要继续往下跑
if (dis + toll[cur][i] <= ansToll && time + Time[cur][i] <= latestTime && !vis[i])
{
vis[i] = true;
DFS(i, dis + toll[cur][i], time + Time[cur][i]);
vis[i] = false;
}
}
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> vertex >> latestTime && (vertex || latestTime))
{
ansToll = inf;
ansTime = inf;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= vertex; i++)
for (int j = 1; j <= vertex; j++)
cin >> Time[i][j];
for (int i = 1; i <= vertex; i++)
for (int j = 1; j <= vertex; j++)
cin >> toll[i][j];
vis[1] = true;
DFS(1, 0, 0);
vis[1] = false;
cout << ansToll << ' ' << ansTime << endl;
}
return 0;
}