Codeforces 413D 2048(dp)

题目连接:Codeforces 413D 2048

题目大意:2048的游戏,两个相同的数x可以变成一个2*x,先给出n,表示在一个1*n的矩阵上面玩2048,规定每次向左移动,并且每次出现一个,给出序列n,表示出现的块的值,0表示既可以是2也可以是4,问说有多少种可能,使得游戏结束后的最大块的值大于等于2^k。

解题思路:dp[i][j][x]表示第i个位置,值为j,x表示先前有没有出现过大于2^k的数;

这种递增的情况可以直接表示为14(总和,以为后面的2,4如果变大,就肯定能和8想合在一起),然后总和%4就可以知道最后以为是否为2,用于考虑被阻隔的情况。

这种情况状态就为6,但是如果k仅为4的话,它是可以满足的,所以这里的第三维状态就是为了区分这种情况的,因为被阻隔后最后的状态可能不够2^k。

具体递推方程见代码。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 2050;
const int MOD = 1e9+7;

int	n, k, s[N];
int dp[N][N][2];

int solve () {

	for (int i = 1; i <= n; i++) {
		if (s[i] == 2 || s[i] == 0) {
			for (int j = 0; j < N; j++) {
				int p = min(j + 2, 2049);
				dp[i][p][1] = (dp[i][p][1] + dp[i-1][j][1])%MOD;
				dp[i][p][0] = (dp[i][p][0] + dp[i-1][j][0])%MOD;
			}
		}

		if (s[i] == 4 || s[i] == 0) {
			for (int j = 0; j < N; j++) {
				int p = min(j + 4, 2049);

				if (j%4) {
					if (j >= (1<<k))
						dp[i][4][1] = (dp[i][4][1] + dp[i-1][j][0])%MOD;
					else
						dp[i][4][0] = (dp[i][4][0] + dp[i-1][j][0])%MOD;

					dp[i][4][1] = (dp[i][4][1] + dp[i-1][j][1])%MOD;
				} else {
					dp[i][p][0] = (dp[i][p][0] + dp[i-1][j][0])%MOD;
					dp[i][p][1] = (dp[i][p][1] + dp[i-1][j][1])%MOD;
				}
			}
		}
	}

	int ans = 0;
	for (int i = 0; i < 2050; i++) {
		ans = (ans + dp[n][i][1])%MOD;
		if (i >= (1<<k))
			ans = (ans + dp[n][i][0])%MOD;
	}
	return ans;
}

int main () {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf("%d", &s[i]);

	memset(dp, 0, sizeof(dp));
	dp[0][0][0] = 1;
	printf("%d\n", solve());
	return 0;
}

Codeforces 413D 2048(dp),码迷,mamicode.com

时间: 2024-08-03 01:53:04

Codeforces 413D 2048(dp)的相关文章

HDU4945 2048(dp)

先是看错题意..然后知道题意之后写了发dp..无限TLE..实在是不知道怎么优化了,跑了遍数据是对的,就当作理论AC掉好了.. #pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string> #include <vector> #include <cstdio> #include <queue> #include <al

Codeforces 13C Sequence --DP+离散化

题意:给出一个 n (1 <= n <= 5000)个数的序列 .每个操作可以把 n 个数中的某一个加1 或 减 1.问使这个序列变成非递减的操作数最少是多少 解法:定义dp[i][j]为将前i个数变为以j为结尾的非递减序列的最少操作次数. 则有: dp[i][j] = min(dp[i][j], min(dp[i][k]) + Cost(原来第i个位置上的数转换到j))  (1 <= k <= j) 即前i个数以j结尾的状态可以由前i-1个数以小于等于j的k结尾的状态转移过来,取

HDU 4945 2048(DP)

HDU 4945 2048 题目链接 题意:给定一个序列,求有多少个子序列能合成2048 思路:把2,4,8..2048这些数字拿出来考虑就能够了,其它数字不管怎样都不能參与组成.那么在这些数字基础上,dp[i][j]表示到第i个数字,和为j的情况数,然后对于每一个数枚举取多少个,就能够利用组合数取进行状态转移,这里有一个剪枝,就是假设加超过2048了,那么后面数字的组合数的和所有都是加到2048上面,能够利用公式一步求解,这种整体复杂度就能够满足题目了.然后这题时限卡得紧啊.10W内的逆元不先

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

Codeforces 57C Array dp暴力找规律

题目链接:点击打开链接 先是计算非递增的方案, 若非递增的方案数为x, 则非递减的方案数也是x 答案就是 2*x - n 只需求得x即可. 可以先写个n3的dp,然后发现规律是 C(n-1, 2*n-1) 然后套个逆元即可. #include<iostream> #include<cstdio> #include<vector> #include<string.h> using namespace std; #define ll long long #def

Codeforces 455A Boredom (dp)

很裸的dp 状态转移方程 dp[i]=max(dp[i-1],dp[i-2]+dp[i]*i) #include<bits/stdc++.h> using namespace std; long long dp[100020]; int main() { int n,a; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a); dp[a]++; } for(int i=2;i&

Codeforces 176B 经典DP

非常好的一个题目,CF上的DP都比较经典 题意就是 给定一个串A,B,正好执行K次操作,每次操作可以把 A串从中间切开,并调换两部分的位置,问最后得到B串共有多少种不同的切法(只要中间有一次不同,即视为不同) 首先,题目的一个关键点一定要抓到,就是 ,不管怎么切 然后调换位置,其实串根本没变,你把串想成一个环,从某一点分成两部分并且交换位置,其实就是把串的起点变到了该点,这是很关键也是最机智的一点 然后,我们要发现规律,你纸上模拟也行,推理也行.. 我们发现:1.首先原串(即以0号字母开头的)个

bzoj 3851: 2048 dp优化

3851: 2048 Time Limit: 2 Sec  Memory Limit: 64 MBSubmit: 22  Solved: 9[Submit][Status] Description Teacher Mai is addicted to game 2048. But finally he finds it's too hard to get 2048. So he wants to change the rule: You are given some numbers. Every

Nanami&#39;s Digital Board CodeForces - 434B (棋盘dp)

大意: 给定01矩阵, m个操作, 操作1翻转一个点, 操作2求边界包含给定点的最大全1子矩阵 暴力枚举矩形高度, 双指针统计答案 #include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <set> #include <map> #include <string> #include <vector>