动态规划——树规
http://www.cnblogs.com/gq-ouyang/archive/2013/02/26/2933431.html
沒有上司的舞會
給一棵樹,每個節點有一些權值,選擇一些節點,要求父親和兒子不能同時被選,求被選中節點的最大和
一開始想錯了,想成了森林轉二叉樹,后來一想不對,這樣的話在遞歸到某一個節點的時候無法確定自己的上級是否被選擇,從而無法確定兄弟節點選不選,因為有自己不選而兄弟節點選這種情況,所以按照最原始的來,對一個節點枚舉全部子樹,然后判斷就行了,沒必要復雜化
#include<iostream> #include<cstdio> #include<vector> #define maxn 6005 #define inf 1314520 using namespace std; int n,happy[maxn],vis[maxn],dp[maxn][2],root; vector<int> l[maxn]; void input(){cin>>n;for(int i = 1;i <= n;i++){scanf("%d",&happy[i]);}int k,t;for(int i = 1;i < n;i++){scanf("%d%d",&t,&k);//k bossvis[t] = 1;l[k].push_back(t);}for(int i = 1;i <= n;i++){if(!vis[i]) root = i;dp[i][0] = dp[i][1] = -inf;} } void dfs(int now){ dp[now][1] = happy[now];dp[now][0] = 0;for(int i = 0;i < l[now].size();i++){dfs(l[now][i]); dp[now][1] += dp[l[now][i]][0];dp[now][0] += max(dp[l[now][i]][1],dp[l[now][i]][0]);} } int main(){input();dfs(root);cout<<max(dp[root][0],dp[root][1]);return 0; }?
Wikioi 1378 選課
題目描述?Description學校實行學分制。每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。學校開設了N(N<300)門的選修課程,每個學生可選課程的數量M是給定的。學生選修了這M門課并考核通過就能獲得相應的學分。?
在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其它的一些課程的基礎上才能選修。例如《Frontpage》必須在選修了《Windows操作基礎》之后才能選修。我們稱《Windows操作基礎》是《Frontpage》的先修課。每門課的直接先修課最多只有一門。兩門課也可能存在相同的先修課。每門課都有一個課號,依次為1,2,3,…。 例如:?
【詳見圖片】
表中1是2的先修課,2是3、4的先修課。如果要選3,那么1和2都一定已被選修過。 你的任務是為自己確定一個選課方案,使得你能得到的學分最多,并且必須滿足先修課優先的原則。假定課程之間不存在時間上的沖突。
輸入文件的第一行包括兩個整數N、M(中間用一個空格隔開)其中1≤N≤300,1≤M≤N。?
以下N行每行代表一門課。課號依次為1,2,…,N。每行有兩個數(用一個空格隔開),第一個數為這門課先修課的課號(若不存在先修課則該項為0),第二個數為這門課的學分。學分是不超過10的正整數。
輸出文件只有一個數,實際所選課程的學分總數。
樣例輸入?Sample Input7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
13
數據范圍及提示?Data Size & Hint各個測試點1s
思路(Easy sir博客上的):
繼續照著三步的方法判斷:一,題目大致一看,有點像有依賴的背包問題,于是你扭頭就走,關掉了我的《樹規》,打開了崔神犇的《背包九講》。然后你哭了,因為有依賴的背包問題只限定于一個物品只依賴于一個物品,而沒有間接的依賴關系。有依賴的背包問題的模型,根本解決不了。崔神告訴你,這屬于樹規的問題,不屬于他背包的范圍了。好了,回過來,我們接著分析。發現這是一棵樹,還是一棵多叉樹,嗯,很好,確定是樹規了。
然后第二步,建樹,一看數據范圍鄰接矩陣;
第三步動規方程:f[i][j]表示以i為節點的根的選j門課的最大值,然后有兩種情況: i不修,則i的孩子一定不修,所以為0;i修,則i的孩子們可修可不修(在這里其實可以將其轉化為將j-1個對i的孩子們進行資源分配的問題,也屬于背包問題);答案是f[1][m]。問題圓滿解決,一氣呵成。
但……
身為追求完美的苦*程序猿的我們,不可以將它更簡單一點呢?
多叉轉二叉。
因為之前我們說過“在樹的存儲結構上,我們一般選的都是二叉樹,因為二叉樹可以用靜態數組來存儲,并且狀態轉移也很好寫(根節點只和左子節點和右子節點有關系)。”所以轉換成二叉樹無疑是一種不錯的選擇。
我們開兩個一維數組,b[i](brother)&c[i](child)分別表示節點i的孩子和兄弟,以左孩子和右兄弟的二叉樹的形式存儲這樣,根節點之和兩個節點有關系了,狀態轉移的關系少了,代碼自然也就好寫了。
我們依舊f[i][j]表示以i為節點的根的選j門課的最大值,那么兩種情況:1.根節點不選修則f[i][j]=f[b[i]][j];2.根節點選修f[i][j]=f[c[i]][k]+f[b[i]][j-k-1]+a[i](k表示左孩子學了k種課程);取二者的最大值即可。
?
代碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 7 using namespace std; 8 const int maxn = 500; 9 int dp[maxn][maxn],value[maxn],brother[maxn],child[maxn],tmpa,tmpb,n,m; 10 11 void dfs(int item,int room){ 12 if(dp[item][room] >=0) return; 13 if(item == 0 || room == 0){ 14 dp[item][room] = 0; 15 return; 16 } 17 dfs(brother[item],room); 18 for(int k = 0;k < room;k++){ 19 dfs(brother[item],k); 20 dfs(child[item],room-k-1); 21 dp[item][room] = max(dp[item][room],max(dp[brother[item]][room],dp[brother[item]][k] + dp[child[item]][room-k-1] + value[item])); 22 } 23 return; 24 } 25 26 int main(){ 27 memset(dp,-1,sizeof(dp)); 28 cin>>n>>m; 29 for(int i = 1;i <= n;i++){ 30 cin>>tmpa>>tmpb; 31 value[i] = tmpb; 32 if(tmpa == 0) tmpa = n + 1; 33 brother[i] = child[tmpa]; 34 child[tmpa] = i; 35 } 36 dfs(child[n+1],m); 37 cout<<dp[child[n+1]][m]; 38 return 0; 39 } View Code?
?
轉載于:https://www.cnblogs.com/hyfer/p/4842023.html
總結
- 上一篇: Swift - 使用set,get确保索
- 下一篇: oracle查询某个用户下的所有视图