codeforce 1110D Jongmah,dp 个人理解和做法

(又又做不出来)

原题:https://codeforces.com/contest/1110/problem/D

题意:给你一堆数字,你的任务是将这些数字分组,规定3个相同的数字或者3个连号(i,i,i 或 i,i+1,i+2)为一组,问:最多可以分成多少组

一开始觉得可以贪。。贪心(也不知道是不是叫做贪心咯),三个一组优先选,然后再考虑连号的情况。感觉我还是太。。菜了,可能是做题不够多。

其实这种程度的贪心应该很容易想到其错误性啊。

3个 i,i+1,i+2  =  i,i,i    (i+1),(i+1),(i+1)   (i+2),(i+2),(i+2) ,这个应该需要很快想到,这样的话就说明 3个及以上的连号 其实是没啥意义的,可以被 取3个相同数字 代替。那么需要区别 取相同 或者 取连号 哪个更优只需要在i,i+1,i+2 都<3的范围内验证其正确性就好了。假如贪心策略是优先取相同数字,只需要人脑遍历不同情况,明显3,2,2 这样的情况就需要取连号,那么假如贪心策略是优先取连号呢,同样3,1,3这种情况并不是最优解,因此贪心行不通(也可能其实是可以贪心但我看不出来)

不能贪心的话还能干嘛,动态规划啊。

动态规划一般需要将所有状态遍历一边,那就来想一下如何表示状态,也就是dp方程组表示啥,怎么表示,怎么转移。

对于一张牌i,需要使用它的地方只有4种,

i-2,i-1,i

i-1,i,i+1

i.i+1.i+2

i,i,i

当然假如前3种都确定了,那么第四种也好说,假如num[i]表示i的数量,那么第四种=(num[n]-i-j-k)/3

那么用dp[n][i][j][k],表示从开始到第一二三种分法分别是i,j,k的数字n最多的分法的数量(表述的不太好)

那么代码就是:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+7; 
int num[maxn],dp[maxn][3][3][3];
int main(){
	int n,m; cin>>n>>m;
	for(int i=1;i<=n;i++){
		int sto; scanf("%d",&sto);
		num[sto]++;
	}
	for(int i=1;i<=m;i++){
		for(int j=0;j<3;j++){
			for(int k=0;k<3;k++){
				for(int l=0;l<3;l++){
					if(j+k+l>num[i]) continue;
					for(int m=0;m<3;m++){
						dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][m][j][k]+l+(num[i]-j-k-l)/3);
					}
				}
			}
		}
	}
	int res;
	for(int i=0;i<3;i++) res=max(res,dp[m][i][0][0]);
	cout<<res<<endl; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43191865/article/details/88636539
今日推荐