2022/2/26DP专题赛总结
赛题链接:动态规划专题测评(易)
Problem A
- 众所周知,这一题考的是语文。
- 题意为店里共有 \(n\) 中物品,可能有一个或没有,所有物品总价格为 \(k\),求顾客所有可能的消费价格;
- 其实01背包再加一个美妙的判断你就能骗到80分;
80分骗分code
#include<bits/stdc++.h>
using namespace std;
int n,k;
int c[3005],tot=0;
int f[505];
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
		scanf("%d",&c[i]);
	f[0]=1;
	for(int i=0;i<=n;++i)
		for(int j=k;j>=c[i];--j)
			f[j]|=f[j-c[i]];
	int cnt=0;
	int ans[505];
	for(int i=0;i<=k;++i)
		if(f[i] && f[k-i]) ans[++cnt]=i;
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;++i)
		printf("%d ",ans[i]);
	return 0;
}
- 正解:对于每一个状态 \(f[j][t]\),我们将它定义为:在已经凑出 \(j\) 时,能否凑出 \(t\);
 在原有01背包的基础上,再加一重循环枚举 \(t\);
 状态转移方程为:
 \(f_{j+c[i],t+c[i]}|=f_{j,t}\)
 \(f_{j+c[i],t}|=f_{j,t}\)
AC code
#include<bits/stdc++.h>
using namespace std;
int n,k;
int c[3005],tot=0;
int f[1005][1005];
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
		scanf("%d",&c[i]);
	f[0][0]=1;
	for(int i=0;i<=n;++i)
		for(int j=k;j+1;--j)
			for(int t=j;t+1;--t){
				f[j+c[i]][t+c[i]]|=f[j][t];
				f[j+c[i]][t]|=f[j][t];
			}
	int cnt=0;
	int ans[505];
	for(int i=0;i<=k;++i)
		if(f[k][i]) ans[++cnt]=i;
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;++i)
		printf("%d ",ans[i]);
	return 0;
}
Problem B
- 几乎是区间DP的模板了;
- 只有一种特殊情况:当两个区间的首尾两位相同时,操作数取 \([start,end-1]\) 和 \([start+1,end]\) 中较小的那个就可以了;
AC code
#include<bits/stdc++.h>
using namespace std;
int n;
int a[305];
int f[305][305];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	memset(f,0x3f3f3f3f,sizeof(f));
	for(int i=0;i<=n;++i)
		f[i][i]=1;
	for(int l=1;l<n;++l)
		for(int s=1;s+l<=n;++s){
			int e=s+l;
			if(a[s]==a[e])
				f[s][e]=min(f[s][e-1],f[s+1][e]);
			for(int i=s;i<=e;++i)
				f[s][e]=min(f[s][e],f[s][i]+f[i+1][e]);
		}
	printf("%d",f[1][n]);
	return 0;
} 
- 如果你想要双倍经验:P4170【CQOI2007】涂色
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号