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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

不止代码:洛谷P1064 金明的预算方案+P2014选课(依赖背包)

發(fā)布時間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 不止代码:洛谷P1064 金明的预算方案+P2014选课(依赖背包) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 題目描述
  • 總結
  • 解析
    • 解法1
    • 解法2
    • 代碼
    • 解法3
    • 代碼

題目描述

金明的預算方案
選課
金明今天很開心,家里購置的新房就要領鑰匙了,新房里有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎么布置,你說了算,只要不超過 n元錢就行”。今天一早,金明就開始做預算了,他把想買的物品分為兩類:主件與附件,附件是從屬于某個主件的。
如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有 0個、1 個或 2 個附件。每個附件對應一個主件,附件不再有從屬于自己的附件。金明想買的東西很多,肯定會超過媽媽限定的 n 元。于是,他把每件物品規(guī)定了一個重要度,分為 5 等:用整數(shù)1~5 表示,第 5 等最重要。他還從因特網(wǎng)上查到了每件物品的價格(都是 10 元的整數(shù)倍)。他希望在不超過 n 元的前提下,使每件物品的價格與重要度的乘積的總和最大。

總結

本題一般性題解是自己獨立寫的,而且思路很簡潔,復雜度也很好

但是這題WA了好幾次,每次都是寫的時候信心滿滿,交完發(fā)現(xiàn)不對,用數(shù)據(jù)檢測才發(fā)現(xiàn)漏洞(老毛病了。。。)
以后應該改改這個壞習慣,應該先把思路想明白了(至少先拿點好的數(shù)據(jù)測測再交啊! )

解析

解法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序,所以子節(jié)點的dfs序比父節(jié)點小
這樣我們就可以從前往后dp
𝑑𝑝[𝑖][𝑗]?表示前i個點里選j個物品的最大價值
那么轉移就是:

𝑑𝑝[𝑖][𝑗]=max?(𝑑𝑝[𝑖?1][𝑗?1]+𝑣𝑎𝑙[𝑝𝑜𝑠[𝑖]],𝑑𝑝[𝑖?𝑠𝑖𝑧[𝑝𝑜𝑠[𝑖]]][𝑗])

前一個轉移是選當前課
后一個是不選當前課(那么之前就只能選到𝑖?𝑠𝑖𝑧[𝑝𝑜𝑠[𝑖]])
最后答案就是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选课(依赖背包)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。