一、Dijskstra算法

     首先Dijskstra算法和bellmanFord算法相比多了一个要求:边的权值为非负。

  Dijskstra算法其实是一种贪心算法,一般地,贪心算法不能保证找到全局最优点。但是Dijskstra正是充分利用了边的权值非负这一点,进而保证所求结果是全局最优的。关于Dijskstra的正确性证明,可以参见《算法导论》24章单源最短路一章关于Dijskstra的部分,正确性证明看起来还是挺顺的~

 二、问题描述

     POJ 1062http://poj.org/problem?id=1062

     因为是少见的中文题,所以题意就略了。

 三、问题分析

     编程之前需要解决两个问题:

     问题一、图的构造

     问题二、如何转换成运用dijskstra算法求最短路

     问题三、题目中的等级差距限制

     

     问题一:首先考虑,肯定要以物品为节点,然后,我们注意到,物品之间存在相互替换的关系,例如,物品1可以被物品2<5000>和物品3<8000>替换,转换到图,就是节点1分别有一条指向节点2和节点3的边,且边的权重分别对应为50008000。现在还剩下一个问题,那就是物品1本身有价值为10000,这种物品本身的价值如何构造到我们的图中呢?我们可以联想差分约束中图的构造,发现可以把探险家自己记为零点,各个节点都有指向零点的边,权重为各个物品的价值。

     

     问题二:图构造好之后,我们很容易发现,这个问题就是求解从酋长对应的节点到探险家对应节点的最短路的权重。

 

     问题三:设等级差距为M,酋长地位为D,最开始的时候考虑,在Dijskstra的过程中记一下地位的最大值和最小值,每次都判断一下。

后来想到这种不能保证全局最优,例如,输入如下:

                         1 4

                         10000 3 2

                         2 1

                         3 3

                         1000 2 2

                         3 1

                         4 1

                         1000 3 1

                         4 2

                         100 4 0

这个例子正确答案为105,但是如果采用上面的方法,最优为1001

后来也没想出太好的办法,只能,采用枚举所有可能的区间,每个区间调用一次Dijskstra算法。即[D-M+i , D+i] 其中i0M

 四、程序实现

 由于是一边写一边改,所以,程序只能凑活着看了

 1 #include<iostream>
 2 using namespace std;
 3 #define maxn 102
 4 #define INF (0x3f3f3f3f)
 5 
 6 struct item
 7 {
 8     int level,d,flag;  
 9 } items[maxn];   //items【i】存储着第i个物品的主人级别和d值,是否Relax过
10 
11 int edges[maxn][maxn];//有向图,第i行储存着i点邻接的点
12 int M; //等级差距限制
13 int N;//物品数量
14 int LOW,HIGH;
15 
16 void input()
17 {
18     for(int i = 1;i < N+1;i++)
19     {
20         cin >> edges[i][0];
21         cin >> items[i].level;
22         int b = 0;
23         cin >> b;
24         while(b--)
25         {
26             int v = 0;
27             cin >> v;
28             cin>>edges[i][v];
29         }
30     }
31     for(int i = 0;i < N+1;i++)
32     {items[i].d = INF;items[i].flag = 1;}
33     items[1].d = 0;
34 }
35 
36 int ExtractMin()
37 {
38     int min = INF; int k = 0;
39     for(int i = 1;i < N+1;i++)
40         if(items[i].flag && min > items[i].d)
41         {min = items[i].d;k = i;}
42     return k;
43 }
44 
45 void Relax(int index , int i)
46 {
47     if(items[i].d > items[index].d + edges[index][i])
48         items[i].d = items[index].d + edges[index][i];
49 }
50 void dijskstra()
51 {
52     int k = 0;
53     while(k!=N)
54     {
55         int index = ExtractMin();
56         if(index)
57         {
58             items[index].flag = 0;
59             for(int i = 0;i < N+1;i++)
60                 if(items[i].flag)
61                    Relax(index , i);
62         }
63         k++;
64     }
65 }
66 
67 int dijskstra_level()
68 {
69      int i = 0;
70      int result = INF;
71      while(i <= M)
72      {
73          for(int j = 0;j < N+1;j++){items[j].flag = 1;items[j].d = INF;}
74          items[1].d = 0;
75          LOW = items[1].level - M + i;
76          HIGH = items[1].level+i;
77          for(int j = 1;j < N+1;j++)
78              if(items[j].level < LOW || items[j].level > HIGH){items[j].flag = 0;}
79          dijskstra();
80          if(result > items[0].d)
81             result = items[0].d;
82          i++;
83      }
84      return result;
85 }
86 
87 int main()
88 {
89     while(cin >>M>>N)
90     {
91         memset(edges, INF , sizeof(int) * maxn * maxn);
92         input();
93         cout << dijskstra_level() <<endl;
94     }
95     return 0;
96 }
View Code

 五、感想

       自己通过自己独立分析问题,自己独立编程实现,最后这个题目AC,这个节奏还是很好的~

       最后,YZY,我想你!

 

 

posted on 2014-03-20 22:58  Oloo  阅读(411)  评论(0)    收藏  举报