DP背包问题

一.糖果(New Online Judge

#include<bits/stdc++.h>
using namespace std;
int dp[1<<20]; //dp[v]:得到口味为v时需要的最少糖果包数量
int ST[100]; //ST[i]:第i包糖果的口味
int main() {
	int n,m,k;
	cin>>n>>m>>k;
	int tot=(1<<m)-1; //tot:二进制是m个1,表示所有m种口味
	memset(dp, -1, sizeof dp);
	for (int i=0; i<n; i++) {
		int st=0;
		for (int j=0; j<k; j++) {
			int x;
			cin>>x;
			st|=(1<<x-1); //状态压缩
		}
		dp[st]=1; //dp[v]:得到口味为v时需要的最少糖果包数量
		ST[i]=st; //ST[i]:第i包糖果的口味
	}
	for (int i=0; i<=tot; i++) //遍历所有口味组合
		if (dp[i]!=-1) //已存在得到口味i的最少糖果包数量
			for (int j=0; j<n; j++) { //检查给定的n包糖果
				int st=ST[j];
				if (dp[i|st]==-1 || dp[i|st]>dp[i]+1) //状态转移
					dp[i|st]=dp[i]+1;
			}
	cout << dp[tot]; //得到所有口味tot的最少糖果包数量
	return 0;
}

二.Dynasty Puzzles(线性dp和背包 - Virtual Judge (vjudge.net)

/*题意:给出n个字符串,若有两个字符串s1,s2,如果s1的尾和s2的首相同并且s1的首和s2的尾相同,
则s1和s2可以连在一起,问最后能连在一起的最长字符串的长度;

思路:dp[i][j]表示以i开头以j结尾的字符串的长度,对于字符串str,头为a,尾为b,那么可以得到
状态转移方程:dp[i][a]=max(dp[i][a]+strlen(str),dp[i][b]) */
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main() {
	int n,dp[505][505];
	cin>>n;
	memset(dp,0,sizeof(dp));
	while(n--) {
		string s;
		cin>>s;
		int c=s.size();
		int a=s[0]-'a',b=s[c-1]-'a';//用下边0代表a,1代表b(0-25表示a-z) 
		for(int i=0; i<26; i++) {//匹配之前的字符串,判断首尾是否相同 
			if(dp[i][a]) {
				dp[i][b]=max(dp[i][a]+c,dp[i][b]);//把首尾相同的字符串连接起来 
			}
		}
		dp[a][b]=max(dp[a][b],c);//若没有匹配到,给其赋值 
	}
	int ans=0;
	for(int i=0; i<26; i++) {
		ans=max(ans,dp[i][i]);//遍历首部和尾部相同的字符串 
	}
	printf("%d\n",ans);
	return 0;
}


三.Boredom(线性dp和背包 - Virtual Judge (vjudge.net))

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	long long n,m=0,a[100001]={0},dp[100001]={0};
	cin>>n;
	while(n--){
		long long x;
		cin>>x;
		a[x]++;
		m=max(m,x);
	}
	memcpy(dp, a, sizeof a);
	for(int i=2;i<=m;i++){
		dp[i]=max(dp[i-1],dp[i-2]+i*a[i]);
	}
	cout<<dp[m]<<endl;

   return 0;
}

三.Cut Ribbon(线性dp和背包 - Virtual Judge (vjudge.net))


//题意: 给你n单位长度的尺子吧,让你尽可能的分多段,
//使得分得的长度为a或b或c,问你最多能分多少段
#include<iostream>
#include<algorithm> 
using namespace std;
int dp[4001];
int main()
{
	int n,a[3];
	int i,j;
	while(cin>>n>>a[0]>>a[1]>>a[2])
	{
		for(i=0;i<=n;i++)
		{
			dp[i]=-4001;
		}
		dp[0]=0;
		for(i=0;i<3;i++)
		{
			for(j=a[i];j<=n;j++)
			{
				dp[j]=max(dp[j],dp[j-a[i]]+1);
			}
		}
		cout<<dp[n]<<endl;
	}
	return 0;
}



四.路径(P1553 - [蓝桥杯2021初赛] 路径 - New Online Judge (ecustacm.cn)

#include<iostream>
using namespace std;
//辗转相除法(递归)求最大公约数 
int gcd(int a, int b)
{
	return b == 0 ? a : gcd(b,a%b);
 } 
 //求最小公倍数 
 int lcm(int a, int b)
 {
 	return a*b/gcd(a,b);
 }
int main()
{
	//动态规划开辟数组 
	int dp[3000] = {0};
	//求出到每一个点的最短路径,先从第二个点开始 
	for (int i = 2; i <= 2021; i++)
	{
		int minValue = 100000000;//警示****该值要开辟的足够大(至少大于该题答案,题主错在这里) 
		if (i - 21 > 0)//如果大于21,则从i-21开始 
		{
			for (int j = i-21; j < i; j++)
			{
				//到该点的距离为距离小于等于21的任意一点的最短距离(dp[j]) 加上任意一点
				//到该点的距离(即最小公倍数)
				//最短距离就是求出上述 距离的最小值 
				minValue = min(minValue,lcm(j,i) + dp[j]);
			}
		}
		else
		{
			//否则从1开始 
			for (int j = 1; j < i; j++)
			{
				    //同上 
					minValue = min(minValue,lcm(j,i) + dp[j]);
			}
		}
		//该点的最短距离为求得的最小值 
		dp[i] = minValue;
	}
	cout << dp[2021] << endl;
	return 0;
}


五.回路计算(P1554 - [蓝桥杯2021初赛] 回路计数 - New Online Judge (ecustacm.cn))

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M=22;
const int N=1<<M;
ll dp[N][M];
int p[M][M];
int gcd(int a,int b) {
	if(a%b==0) return b;
	return gcd(b,a%b);
}
void getpath() {
	for(int i=1; i<=21; i++) {
		for(int j=1; j<=21; j++)
			if(i==j) p[i-1][j-1]=0;//p[i][j]=1表示i,j互质,互通 
			else if(gcd(i,j)==1) p[i-1][j-1]=1;//p[i][j]=1表示i,j互质,互通 
	}
	return ;
}
int main() {
	getpath();
	dp[1][0]=1;//用二进制数i表示经过点的状态
	for(int i=0; i<1<<21; i++) {// j表示最终到达的点 
		for(int j=0; j<21; j++) {
			if(i>>j&1) {
				for(int k=0; k<21; k++) {
					if(i>>k&1&&p[k][j]) {//K点可以到达j点 
						dp[i][j]+=dp[i-(1<<j)][k];//i-(1<<j)代表上一个状态  
					}
				}
			}
		}
	}
	ll ans=0;
	for(int i=0; i<21; i++) {
		ans+=dp[(1<<21)-1][i];
	}
	printf("%lld\n",ans);
}

六.(A-瓜瓜打游戏(EASY)_浙江农林大学第二十二届程序设计竞赛(同步) (nowcoder.com))

#include<iostream>
#define ll long long
using namespace std;
ll n,p,a[5001],dp[5001][5001];
int main(){
	cin>>n>>p;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dp[i][0]=1;
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++){
	 	dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]*a[i])%p;
	 }
	 for(int i=0;i<=n;i++)
	 cout<<dp[n][i]<<" ";
	
	return 0;
} 

七.砝码称重(P1558 - [蓝桥杯2021初赛] 砝码称重 - New Online Judge (ecustacm.cn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[100005];//表示重量为j的称重能不能实现,取值为1或0
ll w[105];//N个砝码的重量
int main()
{
	ll N;
	cin >> N;
	for (ll i = 1; i <= N; i++) cin >> w[i];
	memset(dp, 0, sizeof(dp));
	dp[0] = 1;
	for (ll i = 1; i <= N; i++) {//考虑每个砝码
		//从大到小考虑每个称重j,j>=w[i]
		//如果从小到大,则意味着w[i]可以加很多次
		for (ll j = 100000; j >= w[i]; j--)//此前没有加w[i],现在考虑加
			//如果此前dp[j - w[i]]为1,则加上w[i]的重量,能达到j,所以dp[j]为1
			dp[j] = max(dp[j], dp[j - w[i]]);
	}
	for (ll i = 1; i <= N; i++) {
		for (ll j = 1; j <= 100000 - w[i]; j++)//此前不放w[i]或放,现在减,相当于放左边和不放
			//如果此前dp[j + w[i]]为1,则减去w[i]的重量,能达到j,所以dp[j]为1
			dp[j] = max(dp[j], dp[j + w[i]]);
	}
	ll ans = 0;
	for (ll i = 1; i <= 100000; i++)
		ans += dp[i];
	cout << ans << endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值