P1858 多人揹包
阿新 • • 發佈:2021-12-12
# 原題連結 :
->https://www.luogu.com.cn/problem/P1858<-
# 題外話 :
好像許多題解區 daolaos 都引用了揹包九講裡面的一些很抽象的語言,題解區第一的 dalao 用了所謂“刷表”方式證明。也很模糊。本題解重講思路。
# 思路 :
此題大致同於 01 揹包;
重點在:
/*核心程式碼*/ for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { int add=0,a=1,b=1; while(add<=k) { if(f[j][a]>f[j-v[i]][b]+w[i]) { x[++add]=f[j][a++]; } else { x[++add]=f[j-v[i]][b++]+w[i]; } } for(int t=1;t<=k;t++) f[j][t]=x[t]; } }
其中 $ f[i][j] $ 表示前 i 個數據最 j 優解;
$ add $ 表示到第幾個優解了 , 所以只要 $ add\le k $ 都可進入狀態轉移;
$ x[i] $ 記錄當前否被以前的替換掉 ( 此處如 01 揹包 ),所以之後還要把 x 裡的值刷回去;
a 與 b 分別記錄現在、前面的優解;
就優解判斷否被更新。
# 完整CODE:
#include<bits/stdc++.h> #defineint long long using namespace std; int f[5001][101],v[10001],w[100001],x[100001]; signed main() { int k,m,n; cin>>k>>m>>n; for(int i=1;i<=n;i++) { cin>>v[i]>>w[i]; } memset(f,~0x3f3f3f3f,sizeof(f)); f[0][1]=0; for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { int add=0,a=1,b=1; while(add<=k) { if(f[j][a]>f[j-v[i]][b]+w[i]) { x[++add]=f[j][a++]; } else { x[++add]=f[j-v[i]][b++]+w[i]; } } for(int t=1;t<=k;t++) f[j][t]=x[t]; } } int sum=0; for(int i=1;i<=k;i++) sum+=f[m][i]; cout<<sum<<'\n'; return 0; }