3 2 0 1 0 2 0 3 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 0 0
5 13
解題思路:
以樹來存儲各城堡之間的依賴關系。
首先說狀態表示,dp[i][j]表示在以i為根節點的子樹上攻破j個城堡可達到的最大收益。
為方便表示,以0號節點為總的根節點。
接下來使用‘背包九講’中‘有依賴的背包問題’一節的思路來做。
‘有依賴的背包’是指要選某些物品,必須先選某件物品。這與本題恰好符合。
設j1號、j2號。。。jn號這n件物品依賴i號物品,那麼可以將這n+1個物品歸為一組,那麼這n+1個物品可能有以下的取法:
只取第i號;
取第i號和第j1號;
取第i號和第j1、j2號;
。。。
取第i號和第j1、j2。。。jn號;
取第i號和第j2號;
取第i號和第j2、j3號;
。。。
。。。
取第i號和第jn號;
可是這種取法效率很低,很多多余情況。可以用01背包的方法來進行優化,即對這n+1種物品進行一次01背包,即可得出所有需要考慮的情況(具體參考‘背包九講’)
不過因為i的第k個子節點jk也可能被jk的子節點所依賴,所以要通過dfs先把jk的工作做好再來處理i
代碼:
#include#include #include #include #include using namespace std; vector map[205]; int val[205],dp[205][205]; int n,m; //dp[i][j]:以第i個節點為起始,攻擊j次的最大獲益 inline int bet(int x,int y) { if(x>y) return x; return y; } void dfs(int bg,int c) { int size=map[bg].size(); dp[bg][1]=val[bg];//只攻擊該節點 for(int i=0;i 1) dfs(map[bg][i],c-1); //若未到最後,則繼續向下深搜,先算好將來所需的數據 for(int j=c;j>1;--j)//當背包容量為c時,j=1時為只選bg { for(int k=0;k<=j-1;++k) { //攻擊了第i個子節點下的k個節點,那麼還剩下j-k次機會攻擊其余的 dp[bg][j]=bet(dp[bg][j],dp[bg][j-k]+dp[map[bg][i]][k]); } } } } int main() { while(cin>>n>>m) { if(n==0&&m==0) break; memset(val,0,sizeof(val)); memset(dp,0,sizeof(dp)); for(int i=0;i<=n;++i) map[i].clear(); int a,b; for(int i=1;i<=n;++i) { scanf("%d%d",&a,&b); map[a].push_back(i); val[i]=b; } dfs(0,m+1);//從‘0’開始 cout<