Codeforces 1110D Jongmah (DP)

题意:你有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;
}

  

原文地址:https://www.cnblogs.com/pkgunboat/p/10356117.html

时间: 2024-11-03 14:28:34

Codeforces 1110D Jongmah (DP)的相关文章

Codeforces 864E Fire(DP)

题目链接 Fire 题意 有n个物品,每个物品的挽救时间代价为ti, 消失时刻为di, 价值为pi. 如果要救某个物品,必须在他消失之前救出来. 同一时刻最多只能救一件物品. 当前耗时为当前已经救出的物品的ti累积. 你需要救出总价值尽可能大的物品,并输出方案. 考虑DP f[i][j]为考虑前i个物品,获得总价值为j的时候,所用时间的最小值. c[i][j]为在搜索到第i件物品,当前总价值为j的时候下一步的价值搜索状态. 则有f[i][j] = f[i - 1][j - a[i].p] + a

Codeforces Round #260 (Div. 1) A. Boredom (DP)

题目链接:http://codeforces.com/problemset/problem/455/A A. Boredom time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Alex doesn't like boredom. That's why whenever he gets bored, he comes up with

Codeforces118D Caesar&#39;s Legions(DP)

题目 Source http://codeforces.com/problemset/problem/118/D Description Gaius Julius Caesar, a famous general, loved to line up his soldiers. Overall the army had n1 footmen and n2 horsemen. Caesar thought that an arrangement is not beautiful if somewhe

hdu 5623 KK&#39;s Number(dp)

问题描述 我们可爱的KK有一个有趣的数学游戏:这个游戏需要两个人,有N\left(1\leq N\leq 5*{10}^{4} \right)N(1≤N≤5∗10?4??)个数,每次KK都会先拿数.每次可以拿任意多个数,直到NN个数被拿完.每次获得的得分为取的数中的最小值,KK和对手的策略都是尽可能使得自己的得分减去对手的得分更大.在这样的情况下,最终KK的得分减去对手的得分会是多少? 输入描述 第一行一个数T\left( 1\leq T\leq 10\right)T(1≤T≤10),表示数据组

Ural 1353 Milliard Vasya&#39;s Function(DP)

题目地址:Ural 1353 定义dp[i][j],表示当前位数为i位时,各位数和为j的个数. 对于第i位数来说,总可以看成在前i-1位后面加上一个0~9,所以状态转移方程就很容易出来了: dp[i][j]=dp[i][j]+dp[i][j-1]+dp[i][j-2]+.......+dp[i][j-9]: 最后统计即可. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <

HDU 4908 (杭电 BC #3 1002题)BestCoder Sequence(DP)

题目地址:HDU 4908 这个题是从m开始,分别往前DP和往后DP,如果比m大,就比前面+1,反之-1.这样的话,为0的点就可以与m这个数匹配成一个子串,然后左边和右边的相反数的也可以互相匹配成一个子串,然后互相的乘积最后再加上就行了.因为加入最终两边的互相匹配了,那就说明左右两边一定是偶数个,加上m就一定是奇数个,这奇数个的问题就不用担心了. 代码如下: #include <iostream> #include <stdio.h> #include <string.h&g

Sicily 1146:Lenny&#39;s Lucky Lotto(dp)

题意:给出N,M,问有多少个长度为N的整数序列,满足所有数都在[1,M]内,并且每一个数至少是前一个数的两倍.例如给出N=4, M=10, 则有4个长度为4的整数序列满足条件: [1, 2, 4, 8], [1, 2, 4, 9], [1, 2, 4, 10], [1, 2, 5, 10] 分析:可用动态规划解题,假设dp[i][j],代表满足以整数i为尾数,长度为j的序列的个数(其中每一个数至少是前一个数的两倍).那么对于整数i,dp[i][j] 等于所有dp[k][j-1]的和,其中k满足:

UVA542 - France &#39;98(dp)

UVA542 - France '98(dp) 题目链接 题目大意:之前题目意思还以为看懂了,其实没看明白,它已经把各个选手分在各自所在的区域里面,这就意味着第一次的PK的分组已经确定,而且冠军必须是从两个左右分区出来的胜利者才有机会pk冠军. 解题思路:那么从1-16这个大的区间内诞生出来的冠军可能是来自左边,也可能是右边,然后再左边右边的子区间递归找出冠军.f[i][l][r]表示l-r这个区间的胜利者是i的概率,那么假设i在区间的最左边,f[i][l][r] = Sum(f[i][l][m

HDU 4968 Improving the GPA(dp)

HDU 4968 Improving the GPA 题目链接 dp,最大最小分别dp一次,dp[i][j]表示第i个人,还有j分的情况,分数可以减掉60最为状态 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int t, avg, n; double dp1[15][405], dp2[15][405]; double get(int x) { if