日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

hdu 1055(贪心)

發布時間:2025/3/16 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 hdu 1055(贪心) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

思路:尋找最大權值,合并這個節點和他的父親節點,記下這兩個節點的拓撲序列,同時新節點的權值為這些節點的算術平均值,直到只有一個節點。因為這個節點必定是訪問該節點的父節點之后第一個訪問的節點。

?證明:

對于每一次新的訪問,我們要計算所有未被訪問的點權之和。顯然這個計算式很繁瑣且不易處理。

ò??思考:根據訪問代價的計算規則,對于根節點,它的權值需要計算1次;對于第2個訪問的點,它的權值需要計算2次;對于第3個點……以此類推。

ò??發現:每個點權的計算次數只與遍歷順序有關!即訪問這個點的時間戳。

?

ò??給定一棵N個有權值的節點的有根樹(默認根節點編號為1)。每個節點的權值為Ci。

ò??現在需要遍歷這棵樹。每個點的訪問代價為這個點的權值與訪問它的時間戳的乘積。

ò??遍歷只能按拓撲序,即訪問i時i的父親必須已經被訪問。

ò??求最小遍歷代價。遍歷代價為每個點的訪問代價之和。

ò??轉化的好處?使代價函數的計算式更加確定且易于計算。

?

ò??定義訪問序列為一個排列P={i1,i2,i3...in-1,in},表示節點的訪問順序。

ò??定義點權序列為T={Ci1,Ci2,Ci3…Cin-1,Cin}。

ò??由簡化問題的貪心解法,我們考慮盡量訪問當前未訪問的點權最大節點i。然而由于拓撲序的限制,我們想要訪問它,必須先訪問它的父親。

ò??猜想:當前點權最大的節點i必定是在訪問它的父親j后立刻訪問,否則得不到最優答案。

ò??猜想是否正確?

?

?????令當前最大節點為i,它的父親為j。在訪問序列P中i的位置為xi,j的位置為xj,假設xi+1<xj。

ò??關鍵:對于所有的k滿足xj<xk<xi,這些點必定不是j的祖先或i的后代。

ò??我們在序列P中交換i和k,P仍然是一個合法的訪問序列。

ò??顯然地,由于Ci是訪問j時所有未訪問點的點權最大值,那么交換后的訪問序列P對應的遍歷代價變小了。

ò??進一步,將i交換到xj+1的位置是最優的。

ò??于是猜想是正確的。

ò??結論1:當前最大節點Ci必定是在訪問它的父親j后立刻訪問。

ò???

ò??由結論1知,在訪問序列中,i和j應該是相鄰的,j在前,i在后。

ò??那么i和j可以合并為一個結點k,k的父節點與j的父結點相同,k的子結點是所有j的子結點和i的子結點。然后用k代替樹中的j和i,這樣形成一棵n-1個結點的樹。

ò??合并的好處?

ò??問題的規模縮小了。

ò??新的子問題:k在新樹的訪問序列中的位置?

?

ò??思考:訪問序列P中k必然在k的后代之前,那么我們需要討論的是k與非k后代的節點的相對位置。現在有兩個選擇:一是先訪問k,然后訪問非k的后代的m個結點(i1,i2,...,im)?;二是先訪問非k的后代的m個結點(i1,i2,...,im),然后訪問k。

ò??我們需要知道怎樣的相對位置使最終代價最小。

ò??當前決策k完成時第二種選擇相對于第一種選擇費用之差為:?

ò??F2-F1=(Ci+Cj)×m-{sigma(Cik)|k=1..m}×2?

ò??也就是說,第二種方案先訪問m個節點,這m個節點相比第一種方案提前了2個時間點,那么減少的費用是2×{sigma(Cik)|k=1..m};后訪問i和j,i,j相比第一種方案延后了m個時間點,增加的費用是(Ci+Cj)×m。?
???

ò??1.標記根節點為第一個訪問的節點。

ò??2.求出當前未訪問節點中的最大點權節點i。

ò??3.將i和它的父親j合并為一個節點,節點權值為兩者權值的算術平均數。在序列P中將j的后繼置為i。同時更新樹的信息。

ò??4.若當前樹中節點數大于1,則轉第2步。

ò??5.樹的大小為1時算法結束。

ò??6.掃描求得的P序列得到答案。

ò??時間復雜度:O(N^2)。


AC:

#include <iostream> #include <cstdio> using namespace std;const int N = 1007; struct node {int t; //time[]int p; //記錄父節點int c; //c[]double w; //w[] }num[N]; int n, r;int find() //查找最大的權值,返回其位置 {int pos;double max = 0;for(int i = 1; i <= n; i++)if(num[i].w > max && i != r) //pos不能為根節點{max = num[i].w;pos = i;}return pos; }int main() {int i, j, a, b, pos, ans, f;while(scanf("%d%d", &n, &r), n||r){ans = 0;for(i = 1; i <= n; i++){scanf("%d", &num[i].c); num[i].w = num[i].c; //初始時w[i]置為c[i];num[i].t = 1; //time[i] 置為1;ans += num[i].c; //初始ans = sum(c[i]);}for(i = 1; i < n; i++){scanf("%d%d", &a, &b);num[b].p = a; //記錄父節點,建立樹關系}i = n;while(i > 1){pos = find(); //找到最大權值的位置num[pos].w = 0; //找到后將之置0,否則影響下次查找。f = num[pos].p; //f為父節點ans += num[pos].c * num[f].t; //將找到的c[pos]*time[f]加入ansfor(j = 1; j <= n; j++)if(num[j].p == pos)num[j].p = f; //如果有節點與pos相連,讓它指向pos的父節點num[f].c += num[pos].c; //C_i = c[該節點] + c[父節點]num[f].t += num[pos].t; //T_i = time[該節點]+time[父節點]num[f].w = (double)num[f].c/num[f].t; //合并后的f節點的權值i--;}printf("%d\n", ans);}return 0; }

總結

以上是生活随笔為你收集整理的hdu 1055(贪心)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。