【背包问题】
【背包问题】

【01背包】
模版题https://www.acwing.com/problem/content/2/
一维:从m~1
模版代码
二维写法
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N][N];
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
}
int ans=dp[n][m];
cout<<ans<<endl;
}
一维写法
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N];
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){//注意滚动数组:从m到1
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
int ans=dp[m];
cout<<ans<<endl;
}
解题过程

题目积累
小苯的Polygon
https://ac.nowcoder.com/acm/contest/104637/E
题目用背包信息点:每根木棍都可以选择用或者不用,如果用的话只能用一次
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int INF=0x3f3f3f3f;
int t;
int n;
/*
凸多边形:最长边<其他边之和
背包dp
考虑枚举三角形最长边mx
从若干个木棍的集合中,找出一个子集,使得子集和严格>mx,同时子集和最小化
如何知道总和为s的自己是否存在?
f[i]:总和为i的集合是否存在
for(int i=mx+1;i<=1e4;i++) if(f[i]){
ans=min(ans,mx+i);
break;
}
*/
void solve(){
cin>>n;
vector<int> a(n+1);
int s=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s=max(s,a[i]);
}
sort(a.begin()+1,a.end());
int ans=INF;
vector<int> dp(s*n+1,0);
dp[0]=1;
//枚举最长边
for(int i=1;i<=n;i++){
//子集和严格>最长边
for(int j=a[i]+1;j<=s*n;j++){
if(dp[j]){
ans=min(ans,a[i]+j);//注意这里要加上最长边!
break;
}
}
//从最大值开始枚举:01背包 从上一个状态转移(只能从前i个棍子里选
for(int j=s*n;j>=a[i];j--){
dp[j] |= dp[j-a[i]];
}
}
if(ans==INF) cout<<"-1"<<endl;
else cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
完全背包
模版题https://www.acwing.com/problem/content/3/
模版代码
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N];
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=v[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m]<<endl;
}
解题过程


题目积累
建筑入门
https://ac.nowcoder.com/acm/contest/101921/E
思路

代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=1e6+10,M=55;
/*
【思路】
满足题目的最小k值->n的排列 求1x1+2x2+...+nxn=n*(n+1)*(2*n+1)/6 -> 比这个还小那就不满足(-1)
差值:k-n*(n+1)*(2*n+1)/6
以此为基础增加各项:从下到上进行增加
- 考虑+1的情况:选择[i,n]+1:n阶排列后缀和 -> (n+i)*(n-i+1)/2
- 考虑选择n个x(加在n行 n-1行 n-2行...加多少) 使得x*(n+i)*(n-i+1)/2全部相加起来=差值
->完全背包求解:
设f[i][j]为在[i,n]下进行加法操作能够达到和为j的一种x/利用[i,n]的贡献使得总和为j(只需要记录可不可行)
->dp结束时 如果f[1][k-最小k值]如果为-1 -> 无解
*/
int n,k;
int dp[M][N],chs[M],ans[M];
int cnt[M];//记录方案数
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
//计算差值
int g=k-(n*(n+1)*(2*n+1)/6);
if(g<0){
cout<<"-1"<<endl;
return 0;
}
for(int i=1;i<=n;i++) chs[i]=(n+i)*(n-i+1)/2;
//初始化
memset(dp,-1,sizeof dp);
dp[n][0]=0;
for(int i=1;i<=g;i++) dp[n+1][i]=-1;//不操作任何数肯定达不到->-1
//从下往上dp
for(int i=n;i>=1;i--){
for(int j=0;j<=g;j++){
//如果底层有方案能够达到,那么上层自然有一个方案是+0
if(dp[i+1][j]!=-1) dp[i][j]=0;
}
//完全背包dp
for(int j=chs[i];j<=g;j++){
if(dp[i][j-chs[i]]!=-1) dp[i][j]=dp[i][j-chs[i]]+1;
}
}
if(dp[1][g]==-1){
cout<<"-1"<<endl;
return 0;
}
//回溯求值
int now=g;
int alt=0;//从第一层开始:要累加
for(int i=1;i<=n;i++){
while(now>=chs[i] && dp[i][now]>0){
cnt[i]++;
now-=chs[i];
}
alt+=cnt[i];
ans[i]=i+alt;
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}
多重背包
解题过程

分组背包
【杂题积累】
Subarray Sum Divisibility
https://atcoder.jp/contests/abc419/tasks/abc419_e
以取模的条件进行转移->类似滚动的方式 要运用
ndp辅助转移!
题目大意

解题思路

代码
const int N=510;
int n,m,l;
int a[N];
int w[N][N];
void solve(){
cin>>n>>m>>l;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=l;i++){
for(int j=i;j<=n;j+=l){
for(int k=0;k<m;k++){
w[i][(a[j]+k)%m]+=k;
}
}
}
vector<int> dp(m,inf_int);
dp[0]=0;
for(int i=1;i<=l;i++){
vector<int> ndp(m,inf_int);
for(int j=0;j<m;j++){
for(int k=0;k<m;k++){
ndp[(j+k)%m]=min(ndp[(j+k)%m],dp[j]+w[i][k]);
}
}
dp=ndp;
}
cout<<dp[0]<<endl;
}

浙公网安备 33010602011771号