/*
http://acm.pku.edu.cn/JudgeOnline/problem?id=1062
解法:虚拟一个节点0,设第i物品的价格为p,则建一条有向边:path[0][i]=p;
若通过i物品,可以价格p得到j物品,则建一条有向边:path[i][j]=p;
然后通过dijkstra求0到1的最短路
特殊性:本题的特殊性为,等级。如果得到等级i的物品,则不能去交换比它等级高,或者比它低m等级的物品
回忆一下dijkstra,是将所有的点集分为“已访问”和“未访问”两个集合(通过v标记数组来表示)
每次,都找到两个集合的最短路径,然后将该路径上“未访问”的点,加入到“已访问”节点中。
然后利用这个新加入的点,对“未访问”的节点进行松弛
直到将目的节点t加入到“已访问”节点,或者无法找到最短路径,算法终止。
dijkstra是基于广搜的策略,与广搜相比,只不过松弛方法有所不同,其余都是一样的。所以第一次找到目的节点t时,已经是最优解了
在本题中,枚举所有的物品。比如现在枚举的是i物品,那么将所有不能与i共存(等级制度)在一条路径上的节点放入到“已访问”节点,这样在dij中
便不会对这些点进行扩展。
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<functional>
using namespace std;
const int INF=(1<<20)-1;
const double EPS=1e-8;
const int N=110;
int m,n;
int v[N];
int path[N][N];
int l[N];
int dijkstra(int s,int t)//s起点,t终点
{
int d[N];
for(int i=1;i<=n;i++)d[i]=path[s][i];
while(true)
{
int Min=INF,mark=-1;
for(int i=1;i<=n;i++)
if(!v[i]&&d[i]<Min){Min=d[i];mark=i;}
if(mark==-1)break;
v[mark]=true;
for(int i=1;i<=n;i++)
if(!v[i]&&d[i]>d[mark]+path[mark][i])
d[i]=d[mark]+path[mark][i];
}
return d[t];
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int p,x,a,b;
while(cin>>m>>n)
{
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
path[i][j]=INF;
for(int i=1;i<=n;i++)//建图
{
scanf("%d%d%d",&p,l+i,&x);
path[0][i]=p;
while(x--)
{
scanf("%d%d",&a,&b);
path[a][i]=b;
}
}
int Min=INF;
for(int i=1;i<=n;i++)//枚举
{
for(int j=1;j<=n;j++)//利用m进行分类
{
if(i==j)continue;
if(l[i]<l[j]||l[i]-l[j]>m)v[j]=true;
else v[j]=false;
}
Min=min(Min,dijkstra(0,1));
}
cout<<Min<<endl;
}
//system("pause");
return 0;
}
本文介绍了一种使用Dijkstra算法解决特殊约束问题的方法,通过引入虚拟节点和考虑物品等级限制,实现了从起点到终点的最优路径查找。
203

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



