http://acm.hdu.edu.cn/showproblem.php?pid=1561
ACboy很喜歡玩一種戰略游戲,在一個地圖上,有N座城堡,每座城堡都有一定的寶物,在每次游戲中ACboy允許攻克M個城堡并獲得里面的寶物。但由于地理位置原因,有些城堡不能直接攻克,要攻克這些城堡必須先攻克其他某一個特定的城堡。你能幫ACboy算出要獲得盡量多的寶物應該攻克哪M個城堡嗎?
O(n*m^2)的復雜度相信大家已經都會了。
(代碼注釋區域的dfs)
這里介紹一種O(n*m)的算法,參考https://www.cnblogs.com/Patt/p/5642181.html與徐持衡的論文。
設dp[i][j]為取根節點到i節點路徑上的所有點,再從這條路徑的左邊上和i的子樹上取j體積的點的最大價值。最后答案為dp[0][m]。
(看起來這個定義沒法想,但是徐的論文給的方法是泛化物品合并……這個方法到現在還沒有看懂。)
對于所謂“路徑的左邊”可理解為dfs序比i小且不在這條路徑上的合法路徑。
(更饒了*2)
有一個看起來很妙的方程dp[u][j]=max(dp[u][j],dp[v][j-1]);
當然在這之前每個兒子我們都需要更新,dp[v][j]=dp[u][j]+w[v],然后把v遞歸跑一遍。
(思考為什么不是dp[u][j+1])
(卡了很久思索出了結果,實際很簡單,對dp[u][j]狀態它根本沒選擇v及其子樹的點,所以初始時dp[v][j]選擇的j個點正好是dp[u][j]的j個點)
#include<cstdio> #include<iostream> #include<vector> #include<queue> #include<cstring> #include<algorithm> #include<map> using namespace std; const int N=250; inline int read(){int X=0,w=0;char ch=0;while(!isdigit(ch)){w|=ch=='-';ch=getchar();}while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X; } struct node{int to,nxt; }e[N]; int cnt,head[N]; int n,m,w[N],dp[N][N]; inline void add(int u,int v){e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt; } /*void dfs(int u){for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;dfs(v);for(int j=m;j>=2;j--){//保證無后效性for(int k=1;k<j;k++){dp[u][j]=max(dp[u][j],dp[u][k]+dp[v][j-k]);}}} }*/ void dfs(int u,int c){if(c<=0)return;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;for(int j=0;j<c;j++){dp[v][j]=dp[u][j]+w[v];}dfs(v,c-1);for(int j=1;j<=c;j++){dp[u][j]=max(dp[u][j],dp[v][j-1]);}} } int main(){while(scanf("%d%d",&n,&m)!=EOF&&n+m){n++;cnt=0;memset(head,0,sizeof(head));for(int i=1;i<n;i++){int u=read()+1,v=i+1;w[v]=read();add(u,v);}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){dp[i][j]=w[i];}}dfs(1,m);printf("%d\n",dp[1][m]);}return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++