前两天集训的时候现场赛考到这道题,一开始看到是中文的,心里那个激动啊,简直难以言表,然后我就埋头看了一下题,几分钟后,心里千万只草泥马在奔腾,这题在讲什么鬼,没看懂,这种感觉简直难以言表。赶紧切题,最后到比赛结束还是没想出来。过了两天,重新看这道题,好像有点明白了,个人感觉这道题相当不错。
题目大意:这道题虽然是中文题,但有些地方需要理解一下。总共有 N 个物品,对着N个物品从 1 到 N 编号,每个物品有 X 个替代品,比如样例所给的 1 号物品有两个替代品,其中一个编号为 2,表示用 2 号物品再加上8000元可以得到 1 号物品,其它类似(当初看到样例愣是没想到有什么联系)。然后题目问最少用多少金币可以得到1号物品,注意题目中有一个等级限制,比如样例所给酋长的等级是 3 ,等级限制是 1,首先需要肯定你一定要跟酋长交易,所以样例所给的等级限制是2~3 和 3~4两种情况。附链接:http://poj.org/problem?id=1062
大体思路:这道题一旦理解清楚了,应该也能想到这是最短路问题(但题目一开始就很难理解)。因为可以 1 号物品有几种替代品,这里可以想象成从 1 出发有几条路可以到达某个结点 n,然后从 n 出发又有几条路可以到达其它地方,最后求的是从 1 出发到达其它各点最短的是多远。但题目有个限制就是等级,所以一开始可以将这些不满足等级限制的结点标识成已访问,这个问题就解决了。不过还有一个问题,就是怎么分类?这个可以将所有情况枚举,就样例所给的,先枚举等级 2~3 的情况,求出最短路后,再枚举等级 3~4 的情况,再求出最短路,然后两个最短路取最小输出。
以下是ac代码
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=105;
const int inf=1e9;
int box[maxn][maxn];
int level[maxn];
int price[maxn];
int vis[maxn];
int n,m;
int dijkstra(int start){
int dist[maxn];
for(int i=1;i<=n;i++) //这里需要注意,初始化为inf,不能为box[1][i],就因为这里wa两次,查了一个多小时
dist[i]=inf;
dist[1]=0; //初始化为0,以便第一次循环从1开始
for(int i=0;i<n;i++){
int Min=inf,k=-1;
for(int j=1;j<=n;j++)
if(!vis[j]&&dist[j]<Min){
Min=dist[j];
k=j;
}
if(k==-1)
break;
vis[k]=1;
for(int j=1;j<=n;j++)
if(!vis[j]&&dist[j]>dist[k]+box[k][j])
dist[j]=dist[k]+box[k][j];
}
int Min=inf;
for(int i=1;i<=n;i++) //计算最短路时,记得加上原先的价格
Min=min(Min,price[i]+dist[i]);
return Min;
}
int main(){
while(cin>>m>>n){
int k;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
box[i][j]=inf;
for(int i=1;i<=n;i++){
cin>>price[i]>>level[i]>>k;
for(int j=1;j<=k;j++){
int a,b;
cin>>a>>b;
box[i][a]=b;
}
}
int Min=inf;
for(int i=0;i<=m;i++){ //枚举等级,一开始还担心超时。
memset(vis,0,sizeof(vis)); //全部标识为未访问
for(int j=1;j<=n;j++)
if((level[1]-m+i)>level[j]||level[j]>(level[1]+i)) //等级之外标识为已访问
vis[j]=1;
Min=min(Min,dijkstra(1));
}
cout<<Min<<endl;
}
return 0;
}