题意:给出一堆数字,可以选择三个一样的或者三个连着的,问最多能选多少对。
这题是真没想出来。
解:首先怎么选和连着的无关,和数字有关。所以排序后再选。一看1e6,很好应该是这样。dp[i]表示选到第i个最多的个数。现在加入i+1,那么能新凑出的对数显然和上一次剩了多少i和i-1有关。设dp[i][j]为选到第i个,i剩了j个的数量。眉头一皱发现这样数组会炸,先不管它。dp[i][j]会和dp[i-1],dp[i-2]都有关,但i-1和i-2之间不一定匹配得上。(此处插入正解)于是试图先贪心一波再dp。假设先分递增的,2 2 2 3 3 3 4 hack掉了。先分三个的样例都过不去。放弃挣扎。
/
正解:那么就把它们都写进状态里。i在递增里面只有三种可能,i-2,i-1,i,i-1,i,i+1,i,i+1,i+2。现在写剩余的不好写了,dp[i][j][k][l]表示到i为止,第1/2/3个状态有j/k/l个时的最大值。开始转移。新加进来一个,是i-1,i,i+1的i+1。至于i,i+1,i+2的i+2,反正到这一轮会推到。然后发现i-2,i-1,i这个状态没什么用,有碍视听,删掉,改成dp[i][k][l]。接下来可以转移了。
先不管i有几个。dp[i][k][l]首先可以花一部分的i,准确来说是j+k个,去继承dp[i-1][j][k],然后再花l个去给后面的留接口。注意这部分i是假设后面能凑出来一个顺子,实际上可能凑不出来,但因为有可能要用到所以还得留。最终答案只能看dp[m][0][0]的。最后加上剩下的个数除以三。
预先要用的刚好是j+k+l个,所以加个判断,num[i]>=i+k+l,不然这个状态不存在。
这题,又要从前面的找,又要给后面的留可能性,怪麻烦的。但是代码很好写,适合期末的时候做()
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxx 1000005 #define eps 0.00000001 #define inf 0x3f #define mod 1000000007 //#define int long long int n,m; int dp[maxx][3][3]={0},num[maxx]={0}; signed main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ int t; scanf("%d",&t); num[t]++; } 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]) dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+l+(num[i]-j-k-l)/3); } } printf("%d\n",dp[m][0][0]); return 0; } // i-1 i i+1 i i+1 i+2
浙公网安备 33010602011771号