这道题搁置了很久了……这几天终于想起来把它做了一下,发现有很多小细节没有注意到,WA了好多发,在这里记录一下。
题目是这样的:
题目思路:
其实看着它的数据量这么小,就能想到BFS或者DFS(要不然出题人不会这么心慈手软的哭)。根据题目所述,这是一个图上的BFS问题,我们随着懒汉的路径走一遍,由于我们要求的跟加油次数、加油量有关系,所以重点关注这两个量。可以发现,每走到一个城市,出发点、到达点、已加油量和已加油次数共同构成了一个状态点,我们的BFS就是在这些状态点构成的图上进行的。
除了走迷宫之类的比较明显的BFS,很多BFS都需要我们从状态中抽象出来树或者图去做,比如那个三个杯子倒水的问题,也是这样的。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#define maxn 105
using namespace std;
const int INF = 0x3f3f3f3f;
struct Rod{
int y, len;
};
struct Stus{
int s, num, vol, hav;//出发点 加油次数 已加油量 剩余油量
friend bool operator < (const Stus a, const Stus b){
if(a.num != b.num)
return a.num > b.num;
else if(a.vol != b.vol)
return a.vol > b.vol;
else if(a.hav != b.hav)//选剩余油多的
return a.hav < b.hav;
}
}mx[maxn];
vector<Rod>v[maxn];
priority_queue<Stus>pq;
int s, t;
int n, m, l, y;
int fin;
bool bfs()
{
pq.push((Stus){s, 0, 0, l});
bool flag = false;
while(!pq.empty()){
Stus smid = pq.top();
pq.pop();
bool flag = false;
if(smid.vol < mx[smid.s].vol){
flag = true;
mx[smid.s].vol = smid.vol;
}
if(smid.num < mx[smid.s].num){
flag = true;
mx[smid.s].num = smid.num;
}
if(smid.hav > mx[smid.s].hav){
flag = true;
mx[smid.s].hav = smid.hav;
}
if(!flag)
continue;
if(smid.s == t){
fin = smid.vol;
return true;
}
for(int i = 0; i < v[smid.s].size(); i++){
Rod &rmid = v[smid.s][i];
if(rmid.len > l)
continue;
int cost = rmid.len;
Stus snxt;
if(smid.hav >= cost){
snxt.s = rmid.y;
snxt.num = smid.num;
snxt.vol = smid.vol;
snxt.hav = smid.hav - cost;
pq.push(snxt);
//pq.push((Stus){rmid.y, smid.num, smid.vol, (smid.hav - cost)});
}
snxt.s = rmid.y;
snxt.num = smid.num + 1;
snxt.vol = smid.vol + (l - smid.hav);
snxt.hav = l - cost;
//printf("%d %d %d %d\n", snxt.s, snxt.num, snxt.vol, snxt.hav);
pq.push(snxt);
}
}
return false;
}
int main(void)
{
while(scanf("%d%d%d%d", &n, &m, &l, &y) != EOF)
{
scanf("%d%d", &s, &t);
int a, b, c;
for(int i = 0; i < 103; i++){
v[i].clear();
mx[i] = (Stus){i, INF, INF, -1};
}
for(int i = 0; i < m; i++){
scanf("%d%d%d", &a, &b, &c);
v[a].push_back((Rod){b, c});
v[b].push_back((Rod){a, c});
}
//memset(vis, false, sizeof(vis));
while(!pq.empty()){
pq.pop();
}
fin = 0;
if(bfs()){
printf("%d\n", fin * y);
}else{
printf("-1\n");
}
}
}
/*
5 6 6 1
1 5
1 2 1
1 3 2
2 3 3
2 4 4
4 5 3
3 5 6
*/
注意点:
做这道题时,有两个需要注意的,
1. 第一个是我一开始把两个点之间设置了一个vis数组,就是假如我已经从1->2了,那么设置vis[1][2] = vis[2][1] = true,设置这个的目的是不要让从1->2之后,又在遍历2的时候走回到1。但是仔细想想这样是不对的。
2. 然后就是mx数组的使用(见代码),这个数组的作用是进行优化剪枝,也就是说如果在相同的出发点和目的地,已经有了比当前取出的更适合的状态,那么就没必要再对这个状态进行操作。如果不剪枝,会Memory Limit。