不止代码:洛谷P1064 金明的预算方案+P2014选课(依赖背包)
文章目錄
- 題目描述
- 總結
- 解析
- 解法1
- 解法2
- 代碼
- 解法3
- 代碼
題目描述
金明的預算方案
選課
金明今天很開心,家里購置的新房就要領鑰匙了,新房里有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎么布置,你說了算,只要不超過 n元錢就行”。今天一早,金明就開始做預算了,他把想買的物品分為兩類:主件與附件,附件是從屬于某個主件的。
如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有 0個、1 個或 2 個附件。每個附件對應一個主件,附件不再有從屬于自己的附件。金明想買的東西很多,肯定會超過媽媽限定的 n 元。于是,他把每件物品規定了一個重要度,分為 5 等:用整數1~5 表示,第 5 等最重要。他還從因特網上查到了每件物品的價格(都是 10 元的整數倍)。他希望在不超過 n 元的前提下,使每件物品的價格與重要度的乘積的總和最大。
總結
本題一般性題解是自己獨立寫的,而且思路很簡潔,復雜度也很好
但是這題WA了好幾次,每次都是寫的時候信心滿滿,交完發現不對,用數據檢測才發現漏洞(老毛病了。。。)
以后應該改改這個壞習慣,應該先把思路想明白了(至少先拿點好的數據測測再交啊! )
解析
解法1
由于本題的特殊限制,對于每個主件,最多可以分為五種情況:
1.都不買
2.只買主件
3.買主件與附件A
4.買主件與附件B
5.買主件與附件A、B
然后就可以轉化為01背包了
但是我們豈可滿足于此!
解法2
當附件很多且存在附件之后還有附件時應該怎么辦呢?
深思熟慮后:啊哈!
我們把dp開成二維
dp[k][w]表示第k層依賴關系下,物品必須購買,w的錢獲得的最大價值
對于處于第k層的物品A,枚舉它的所有附件,將其作為第k+1層遞歸處理
然后嘗試用第k層的dp值更新k-1層的dp值
最后,dp[0][n]就是答案
因為每個物品最多處理一次,單層遞歸復雜度是n
所以總時間復雜度為m*n(和01背包一樣啊有木有!)
不難看出,空間復雜度也是n*m
代碼
#include <bits/stdc++.h> using namespace std; const int N=1e6+100; int n,m; struct node{int w,v,belong;int nxt[500],nm; }p[500]; int a,b,c; int f[80][34050]; int jd[500]; void Try(int k,int id){for(int i=0;i<=n;i++){if(i<p[id].w) f[k][i]=-2e9;else f[k][i]=f[k-1][i-p[id].w]+p[id].v;}for(int i=1;i<=p[id].nm;i++){Try(k+1,p[id].nxt[i]);}for(int i=0;i<=n;i++) f[k-1][i]=max(f[k-1][i],f[k][i]); } void print(){for(int i=1;i<=n;i++) printf("%d ",f[0][i]);printf("\n");return; } int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&p[i].w,&p[i].v,&p[i].belong);p[i].v*=p[i].w;if(p[i].belong) p[p[i].belong].nxt[++p[p[i].belong].nm]=i;}for(int i=1;i<=m;i++){if(p[i].belong) continue;Try(1,i); // print();}printf("%d",f[0][n]);return 0; } /* 5 5 1 100 0 2 1 0 1 1000 2 2 2 2 6 10000 0 */解法3
樹上dfs一邊求出size和dfs序(注意是后序!)
因為后序dfs序,所以子節點的dfs序比父節點小
這樣我們就可以從前往后dp
𝑑𝑝[𝑖][𝑗]?表示前i個點里選j個物品的最大價值
那么轉移就是:
前一個轉移是選當前課
后一個是不選當前課(那么之前就只能選到𝑖?𝑠𝑖𝑧[𝑝𝑜𝑠[𝑖]])
最后答案就是dp[n][m]
代碼
#include <bits/stdc++.h> using namespace std; const int N=1800; int n,m; /* op=0 被自己覆蓋 op=1 被兒子覆蓋 op=2 被父親覆蓋 */ struct node{int to,nxt; }p[N]; int fi[N],cnt=-1,ru[N]; int v[N],belong[N]; void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt; } int a,b,c; int pos[N],dfs[N],size[N],tot; void build(int x,int fa){size[x]=1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;build(to,x);size[x]+=size[to];}dfs[x]=++tot;pos[tot]=x; } int dp[N][N]; int main(){memset(fi,-1,sizeof(fi));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d%d",&belong[i],&v[i]);if(belong[i]) addline(belong[i],i);else addline(0,i);}build(0,-1);for(int i=1;i<=tot;i++){int id=pos[i];for(int j=m;j>=1;j--){dp[i][j]=max(dp[i-1][j-1]+v[id],dp[i-size[id]][j]);}}printf("%d",dp[n][m]);return 0; } /* 6 1 30 3 2 3 4 2 16 2 5 6 3 5 0 4 4 0 5 11 0 6 5 0 */總結
以上是生活随笔為你收集整理的不止代码:洛谷P1064 金明的预算方案+P2014选课(依赖背包)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PC端网易云音乐的歌曲怎么保存到我的音乐
- 下一篇: 警卫站岗(树上dp)