USACO Sweet Butter 與圖的尋徑算法

此題的實際模型如下:給定一個有P個頂點的無向含權圖,選擇一個頂點,使得從該點到給定的N個頂點的權值之和最小。

最經典的就是Dijkstra算法了。這個經典的不能再經典的貪心策略,應該都知道的吧。每次都需要在Distance數組中尋求一個最小值,如果圖很大,則在此處花費的時間會很多,而堆則可以在O(1)的時間內求得最小值,並且維護一個堆所需的時間都是在log級的。因此考慮把Distance數組的值用堆來存儲。

用堆操作的Dijkstra的思想還是與一般的思想類似:

初始化Distance[]=MAX,然後將源點t放入堆中(這就與Distance[t]=0類似),再從堆中找出一個最小值沒有被確定的點minp(就是Distance[minp]=MAX),將其確定下來,Distance[minp]=mins,mins爲從堆中取出來的那個最小值,接着由這個最小值所連接的點求出從源點到這些點的路徑長,若所連接的點沒有求出最小值,Distance[i]=MAX,則將這個點放入堆中,這就好比用for循環去修改每一個Distance[]的值,不同的地方在於堆中存放的是源點到各個頂點的最小值,如果剛求出來的頂點在Distance[]中已經被賦值了,說明求出來的肯定不是最小值,就像普通Dijkstra的Mark[]一樣,mark過的點是不能再被計算的,所以不能放Distance[]中有值的點。這樣Dijkstra的部分就完成了。

Dijkstra算法真的是簡潔優雅的典範,也可以看出,大學裏的《數據結構》這麼課學好了,走出學校後確實是很厲害的,尋徑算法在現實中出現頻率還是很高的。

另外一種方法是改進的BFS算法:

可以理解爲動態規劃,不過也不是嚴格的動態規劃。你可以寫出下面的遞推式:

Distance(start, now) = Min(Distance(start, k) + Distance(k, now),  for k in start's adjacnet list)

但如果這個式子是有問題的,請見下面的廣搜加深搜的說明。但有問題可以想辦法解決,一種辦法是:對於枚舉的每個牧場 i,令Distance[j]表示 i 到 j 的距離,初始值爲INT_MAX,其中Distance[i]=0。維護一個隊列,初始爲q[1]=i,由隊首枚舉其他的牧場 j 更新牧場 i 到 j 的最短距離並同時拓展隊列。直到隊列空爲止。這樣就求出了點i到所有點的最短距離。和BFS差不多吧,但不同的是寬度優先搜索中一個點出了隊列就不可能重新進入隊列,這個算法中一個點可能在出隊列之後再次被放入隊列,也就是一個點改進過其它的點之後,過了一段時間可能本身被改進,於是再次用來改進其它的點,這樣反覆迭代下去。

其實也可以廣搜加上深搜:

基本思路還是和上面說的BFS一致,主要還是因爲一個點被修改後,位於它上層的點也可能受到影響,它上層的上層的點也可能收到影響,這個就是DFS的回溯的。

  1. /*
  2. ID:fairyroad
  3. LANG:C++
  4. TASK:butter
  5. */
  6. #include<fstream>
  7. #include<cstring>
  8. #include<queue>
  9. #include <limits>
  10. using namespace std;
  11.  
  12. ifstream fin("butter.pushed");
  13. ofstream fout("butter.out");
  14.  
  15. const int MAX = 805;
  16.  
  17. struct vertex
  18. {
  19.         int end,len;
  20. };
  21. vertex adj[MAX][MAX];
  22.  
  23. int cnt[MAX]={0}, cowpos[505]={0}, n, p, c;
  24. bool pushed[MAX]={0};
  25. int distance[MAX];
  26.  
  27. int search(int start)
  28. {
  29.         memset(pushed, 0sizeof(pushed));
  30.         for(int k = 1; k <= p; k++)
  31.                 distance[k] = numeric_limits<int>::max();
  32.        
  33.         queue<int> Q;
  34.         Q.push(start);
  35.         pushed[start] = true;
  36.         distance[start] = 0;
  37.         while(!Q.empty())
  38.         {
  39.                 int x = Q.front();
  40.                 Q.pop();
  41.                 pushed[x] = false;
  42.                 for(int j = 0; j < cnt[x]; j++)
  43.                 {
  44.                         if(distance[x]+adj[x][j].len < distance[adj[x][j].end])
  45.                         {
  46.                                 distance[adj[x][j].end] = distance[x]+adj[x][j].len;  
  47.                                 if(!pushed[adj[x][j].end])
  48.                                 {
  49.                                         Q.push(adj[x][j].end);
  50.                                         pushed[adj[x][j].end] = true;
  51.                                 }
  52.                         }
  53.                 }
  54.         }
  55.        
  56.         int ans = 0;
  57.  
  58.         for(int i = 1; i<=n; i++)
  59.         {
  60.                 if(distance[cowpos[i]]==numeric_limits<int>::max()) return -1;
  61.                 else  ans+=distance[cowpos[i]];
  62.         }
  63.         return ans;
  64. }
  65.  
  66. int main()
  67. {
  68.         memset(cnt, 0sizeof(cnt));
  69.         fin>>n>>p>>c;
  70.         for(int i = 1; i<=n; i++)
  71.                 fin>>cowpos[i];
  72.  
  73.         for(int i = 1, s, t, value; i <= c; i++)
  74.         {
  75.                 fin>>s>>t>>value;
  76.                 adj[s][cnt[s]].end = t; adj[s][cnt[s]].len = value; cnt[s]++;
  77.                 adj[t][cnt[t]].end = s; adj[t][cnt[t]].len = value; cnt[t]++;
  78.         }
  79.  
  80.         int res, min = numeric_limits<int>::max();
  81.         for(int i = 1; i <= p; i++)
  82.         {
  83.                 res = search(i);
  84.                 if(res < min && res != -1) min  =  res;
  85.         }
  86.        
  87.         fout<<min<<endl;
  88.         return 0;
  89. }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章