1. 程式人生 > >Codeforces 1110D Jongmah (DP)

Codeforces 1110D Jongmah (DP)

ces pri 選擇 mes spa cpp sin 假設 其余

題意:你有n個數字,範圍[1, m],你可以選擇其中的三個數字構成一個三元組,但是這三個數字必須是連續的或者相同的,每個數字只能用一次,問這n個數字最多構成多少個三元組?

解析:首先我們容易發現,我們發現,假設有3個三元組(x, x + 1, x + 2),我們不妨把這3個三元組換成(x, x, x), (x + 1, x + 1, x + 1), (x + 2, x + 2, x + 2)這3個三元組。那麽,對於每個x,最多有2個(x, x + 1, x + 2)這樣的三元組。這樣,每個階段的狀態數就是有限的了。

設dp[i][j][k]為數字1到i,有j個(i - 1, i , i + 1)三元組,k個(i , i + 1, i + 2)三元組可以組成的最多的三元組的數目,我們現在考慮向i + 1轉移。因為前面有j個(i - 1, i , i + 1),k個(i , i + 1, i + 2),假設本來有t個i + 1,那麽現在可用來構成新的三元組的i + 1有t - j - k個。

這t - j - k個i + 1可以用來構成(i + 1, i + 2, i + 3),也可用來構成(i + 1, i + 1, i + 1)。我們就可以枚舉這些i + 1是形成什麽樣的三元組來進行狀態轉移了。初態:dp[0][0][0] = 0,其余負無窮。末態:dp[m + 1][0][0]。因為m + 1肯定沒有數,所以dp[m + 1][0][0]是m + 1處的唯一合法狀態,也就是答案。

代碼:

#include <bits/stdc++.h>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define lowbit(x) (x & (-x))
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1000010;
int a[maxn], cnt[maxn];
int dp[maxn][3][3];
int main () {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		cnt[a[i]]++;
	}
	memset(dp, -0x3f, sizeof(dp));
	dp[0][0][0] = 0;
	for (int i = 0; i <= m + 1; i++) {
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++) {
				if(dp[i][j][k] < 0) continue;
				int now = cnt[i + 1] - j - k;
				for (int t = 0; t < 3 && t <= now; t++) {
					dp[i + 1][k][t] = max(dp[i + 1][k][t], dp[i][j][k] + (now - t) / 3 + t);
				}
			}
	}
	printf("%d\n", dp[m + 1][0][0]);
	return 0;
}

  

Codeforces 1110D Jongmah (DP)