POJ 1062 昂贵的聘礼(Dijkstra)

时间:2022-10-22 07:34:06

题意 : 真真是做POJ第一次遇到中文题,好吧,虽然语言通了,我一开始也没看懂样例什么意思,题意的话就是说这个探险家想娶酋长的女儿,但是没有钱,酋长说他可以用祭司的水晶球或者皮袄来换取少花一部分钱,同样的祭司也提出了类似的要求。最后输出能够花的最少的钱去迎娶酋长的女儿。

这个题需要注意的点是:1.等级问题,等级相差过大的话,不与探险家交换;

2.而每一个物件,编号自1到n是已经默认的,所以不用再去赋值或者迷惑了。。

思路 :就是一个最短路的问题,Dijkstra还有 Bellman ford 以及spfa都可以去求,不过倒是不知道为什么,大神们统一用的Dijkstra去做的,以致我一度认为这个题必须用这个做,后来问了秦老师,说是哪个都可以,本题是用的Dijkstra方法做的。其实就是把这个转化为一个图,建图就行了,不过应该注意的点是,每一个结点也是有值的,做的时候要别忘了加上再进行比较。

 #include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
const int maxn = ;
using namespace std;
const int oo =<<;
int map[maxn][maxn];//map数组里存的是整个图中每两个点之间那条边的权值
int dis[maxn];//dis数组里存的是每次更新的最小值
int vis[maxn];//标记数组,表示某个点是否已被访问
int m,n;//地位等级差距限制以及物品总数
int pric[maxn],l[maxn],coun;//每个物品的价值,物品主人的等级,这个物品的替代品数
int num,repl;//替代品的编号,优惠后的价格
int max1=oo;//初始化
int i,j,k,h; /*Dijkstra Algorithm*/
void dijkstra(int s)
{
memset(vis,,sizeof(vis));
for(k = ; k <= n ; k++)
dis[k] = oo;//初始化
dis[] = ;
for(k = ; k <= n ; k++)
{
int u,min = oo ;
for(j = ; j <= n ; j++)
{
if(!vis[j] && dis[j]<=min && l[j]<=s+m && l[j]>=s)//该点没被访问过,并且小于min,物品主人没有超出等级的限制
{
min = dis[j]; //双重循环在找每个dis的最小值
u = j;//记下这个点的下标以用于下边dis数组的更新
}
}
vis[u] = ;//标记结点已被访问
// if(min == oo)
// break;
for(j = ; j <= n ; j++)
{
if(l[j]>=s && l[j]<=s+m && map[u][j]!=oo && dis[j]>=dis[u]+map[u][j])
//没有超出等级限制,并且这两点是有边的,还满足比他小
{
dis[j] = dis[u]+map[u][j] ;//dis数组的更新
}
}
}
int c = pric[];
for(j = ; j <= n ; j++)
{
//因为每个节点本身是有值的,如果更新到某个结点再加上这个点物品的价值比原来那个点的钱还多,那就没有要换的必要了
if(dis[j]==oo)
continue;
if(dis[j]+pric[j] < c)
c = dis[j]+pric[j];
}
max1 = min(c,max1);
}
void init()
{
for(i = ; i <= n; i++)
for(j = ; j <= n ; j++)
map[i][j]=map[j][i]=oo;
}
int main()
{
while(~scanf("%d%d",&m,&n))
{
init();
for(i = ; i <= n ; i++)
{
scanf("%d %d %d",&pric[i],&l[i],&coun);
for(j = ; j <= coun ; j++)
{
scanf("%d %d",&num,&repl);
if(map[i][num]>repl)
map[i][num]=repl;
}
}
int s;
int s=max(l[]-m,);//筛选掉等级差别过大的那些等级
/*if(l[1]-m > 0)
s = l[1]-m;
else s = 0;*/
for(i=s; i<=l[]; i++)//每个点遍历一遍
dijkstra(i);
printf("%d\n",max1);
}
return ;
}

Dijkstra算法的话还算是比较好理解的,其基本步骤就是:

1、把所有结点分成两组:
      第一组:包括已经确定最短路径的结点;
      第二组:包括尚未确定最短路径的结点。
2、开始时,第一组只包含起点,第二组包含剩余的点;
3、用贪心的策略,按最短路径长度递增的顺序把第二组的结点加到第一组去,直到v0可达的所有结点都包含于第一组中。在这个过程中,不断更新最短路径,总保持从v0到第一组各结点的最短路径长度dist都不大于从v0到第二组任何结点的路径长度。
4、每个结点对应一个距离值,第一组结点对应的距离就是v0到此结点的最短路径长度,第二组结点对应的距离值就是v0由第一组结点到此结点的最短路径长度。
5、直到所有的顶点都扫描完毕(v0可达的所有结点都包含于第一组中),找到v0到其它各点的所有最短路径。

详细可以参考大神的博客http://www.cnblogs.com/mycapple/archive/2012/08/12/2634227.html,有图有字生动形象

这个是算法邻接表的模板,秦老师写的:

 #include <stdio.h>
#include <string.h> const int maxn = ;
const int maxm = ;
const int oo = <<;
struct node{
int u;
int v;
int w;
int next;
}edge[maxm];
int head[maxn];
int dis[maxn];
int cnt;
int n, m; void add(int u, int v, int w){
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
} void dijkstra(int s){
bool vis[maxn];
for(int i = ; i < n; i++){
dis[i] = oo;
vis[i] = false;
}
dis[s] = ;
for(int i = ; i < n; i++){
int minn = oo;
int u = s;
for(int j = ; j < n; j++){
if(!vis[j] && minn > dis[j]){
minn = dis[j];
u = j;
}
}
for(int j = head[u]; j != -; j = edge[j].next){
int v = edge[j].v;
int newdis = minn + edge[j].w;
if(!vis[v] && newdis < dis[v]) dis[v] = newdis;
}
vis[u] = true;
}
} void init(){
cnt = ;
memset(head, -, sizeof(head));
} int main(){
while(~scanf("%d %d", &n, &m)){
int u, v, w;
init();
for(int i = ; i < m; i++){
scanf("%d %d %d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
scanf("%d %d", &u, &v);
dijkstra(u);
if(dis[v] < oo) printf("%d\n", dis[v]);
else puts("-1");
}
return ;
}

这个代码是杭电1784的代码