0
点赞
收藏
分享

微信扫一扫

动态规划总结(长期更新中)

钎探穗 2022-03-17 阅读 77

动态规划一直是我的短板,也没有系统的做过题目,不过也是时候好好总结一下动态规划问题了,本贴会长期更新,一边刷luogu题单和codeforces上面的dp类型题目一边把各种基本类型的动态规划及其变形的经典写法做一个记录

1.从n个数字里面选择刚好k个凑出的数有多少种情况

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=110;
int a[maxn],b[maxn];
int dp[maxn][maxn][maxn];
void solve(){
	int n,k;
	cin>>n>>k;
	int sum=0;	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			for(int k=0;k<=sum;k++){
				dp[i][j][k]|=dp[i-1][j][k];  //从前面i个数里面刚好选择j个凑出k
				if(k>=a[i])dp[i][j][k]|=dp[i-1][j-1][k-a[i]];
			}
		}
	}
	cout<<"sum "<<sum<<"\n";
	for(int i=0;i<=sum;i++){
		cout<<"i "<<i<<"val ";
		cout<<dp[n][k][i]<<"\n";
	}
}
signed main(){
	int t=1;
	while(t--){
		solve();
	}
}

2.从n个数里面选择0-k个凑出的数有多少种情况

第一种做法:重点在于 if(j)dp[i][j][k]|=dp[i][j-1][k] 代表前i个里面选择刚好j个等于k或等于前i个里面选择刚好j-1个等于k

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=110;
int a[maxn],b[maxn];
int dp[maxn][maxn][maxn];
void solve(){
	int n,k;
	cin>>n>>k;
	int sum=0;	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			for(int k=0;k<=sum;k++){
				if(j){
					dp[i][j][k]|=dp[i][j-1][k];
				}
				dp[i][j][k]|=dp[i-1][j][k];  //从前面i个数里面刚好选择0-j个凑出k
				if(k>=a[i])dp[i][j][k]|=dp[i-1][j-1][k-a[i]];
			}
		}
	}
	cout<<"sum "<<sum<<"\n";
	for(int i=0;i<=sum;i++){
		cout<<"i "<<i<<"val ";
		cout<<dp[n][k][i]<<"\n";
	}
}
signed main(){
	int t=1;
	while(t--){
		solve();
	}
}

第二种做法:    for(int i=0;i<=n;i++){
        dp[0][i][0]=1;
    }  重点在于初始化的时候要注意从前面0个里面选择i个组成0的方案数有多少种,一开始我以为要把dp[i][j][k]里面的i和j都要变化其实不然,对于dp方程一般i都是由i-1转移过来的所以初始化的时候只要把i=0时候的各种情况初始化好即可i=1,2,3可以由上一个转移过来

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=110;
int a[maxn],b[maxn];
int dp[maxn][maxn][maxn];
void solve(){
	int n,k;
	cin>>n>>k;
	int sum=0;	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	for(int i=0;i<=n;i++){
		dp[0][i][0]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			for(int k=0;k<=sum;k++){
				if(j){
					dp[i][j][k]|=dp[i][j-1][k];
				}
				dp[i][j][k]|=dp[i-1][j][k];  //从前面i个数里面刚好选择0-j个凑出k
				if(k>=a[i])dp[i][j][k]|=dp[i-1][j-1][k-a[i]];
			}
		}
	}
	cout<<"sum "<<sum<<"\n";
	for(int i=0;i<=sum;i++){
		cout<<"i "<<i<<"val ";
		cout<<dp[n][k][i]<<"\n";
	}
}
signed main(){
	int t=1;
	while(t--){
		solve();
	}
}
举报

相关推荐

0 条评论