POJ 1187 陨石的秘密 (线性DP)

题意:

公元11380年,一颗巨大的陨石坠落在南极。于是,灾难降临了,地球上出现了一系列反常的现象。当人们焦急万分的时候,一支中国科学家组成的南极考察队赶到了出事地点。经过一番侦察,科学家们发现陨石上刻有若干行密文,每一行都包含5个整数: 
1 1 1 1 6 
0 0 6 3 57 
8 0 11 3 2845 
著名的科学家SS发现,这些密文实际上是一种复杂运算的结果。为了便于大家理解这种运算,他定义了一种SS表达式: 
1. SS表达式是仅由‘{‘,‘}‘,‘[‘,‘]‘,‘(‘,‘)‘组成的字符串。 
2. 一个空串是SS表达式。 
3. 如果A是SS表达式,且A中不含字符‘{‘,‘}‘,‘[‘,‘]‘,则(A)是SS表达式。 
4. 如果A是SS表达式,且A中不含字符‘{‘,‘}‘,则[A]是SS表达式。 
5. 如果A是SS表达式,则{A}是SS表达式。 
6. 如果A和B都是SS表达式,则AB也是SS表达式。

例如 
()(())[] 
{()[()]} 
{{[[(())]]}} 
都是SS表达式。 
而 
()([])() 
[() 
不是SS表达式。

一个SS表达式E的深度D(E)定义如下: 
 
例如(){()}[]的深度为2。

密文中的复杂运算是这样进行的: 
设密文中每行前4个数依次为L1,L2,L3,D,求出所有深度为D,含有L1对{},L2对[],L3对()的SS串的个数,并用这个数对当前的年份11380求余数,这个余数就是密文中每行的第5个数,我们称之为?神秘数?。 
密文中某些行的第五个数已经模糊不清,而这些数字正是揭开陨石秘密的钥匙。现在科学家们聘请你来计算这个神秘数。

思路:

初始想法:我们令dp[l1][l2][l3][d]为用了l1个小括号,l2个中括号,l3个大括号,深度恰好为d时的方案数,现在我们来找状态之间的联系。然而我们可以发现一个残酷的事实,光用4个变量无法很好的表示一个状态。比如,我们添加一个小括号,当前状态带表的括号序列中,有一部分序列的深度增加了,有一部分没有增加,所以为了正确的转移状态,正常想法就是用状压之类的记录具体方案,然而这个题就。。。

我们可以发现,新添加一个括号,括号序列的深度最多增加1,要么就不变,所以,如果dp[l1][l2][l3][d]表示的是用了l1个小括号,l2个中括号,l3个大括号,深度小于等于d的方案数就很好办了,添加一个括号后从深度小于等于d的状态转移到深度小于等于d + 1的状态。

则等于d的方案数 = 小于等于d的方案数 - 小于等于d - 1的方案数。

还有一个问题,我们怎么不重不漏的写出状态转移的过程?我们可以发现,所有深度小于等于d的括号序列是由若干个深度小于等于d的嵌套的括号构成的,所以,我们可以这样转移状态:我们把当前状态分成2个部分,一个部分用来形成嵌套的括号,另一部分对应的是那个状态的方案数。我们枚举向最里面添加什么括号。因为大括号外面不能有其它的括号,所以当在最里面套大括号时,只能有大括号。例如,当前嵌套形的括号是{[()]},我们不能向里面添加{},但是添加小括号可以,变成{[(())]}。同理,枚举状态时,当添加的是中括号时,外面只能是中括号和大括号。

思路和代码实现参考了这篇博客:https://blog.csdn.net/Flying_Stones_Sure/article/details/7954114

代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int mod = 11380;
int dp[11][11][11][31];
bool v[11][11][11][31];
int dfs(int l1, int l2, int l3, int deep) {
	if (l1 == 0 && l2 == 0 && l3 == 0) {
		v[l1][l2][l3][deep] = 1;
		return dp[l1][l2][l3][deep] = 1;
	}
	if (deep == 0) {
		v[l1][l2][l3][deep] = 1;
		return dp[l1][l2][l3][deep]  = 0;
	}
	if (v[l1][l2][l3][deep])
		return dp[l1][l2][l3][deep];
	int ans = 0;
	for (int i = 0; i <= l3; i++) {
		if (i) {
			ans = (ans + dfs(0 , 0, i - 1, deep - 1) * dfs(l1, l2, l3 - i, deep)) % mod;
		}
		for (int j = 0 ;j <= l2; j++) {
			if (j) {
				ans = (ans + dfs(0, j - 1, i, deep - 1) * dfs(l1, l2 - j, l3 - i, deep)) % mod;
			}
			for (int k = 1; k <= l1; k++) {
				ans = (ans + dfs(k - 1, j, i, deep - 1) * dfs(l1 - k, l2 - j, l3 - i, deep)) % mod;
			}
		}
	}
	v[l1][l2][l3][deep] = 1;
	return dp[l1][l2][l3][deep] = ans;
}
int main() {
	int n, m, d, t;
	while(~scanf("%d%d%d%d", &n, &m, &t, &d)) {
		dfs(n, m, t, d);
		if(d) dfs(n, m ,t, d - 1);
		if(d) {
			printf("%d\n", (dp[n][m][t][d] - dp[n][m][t][d - 1] + mod ) % mod);
		} else {
			printf("%d\n", dp[n][m][t][d]);
		}
	}
}

  

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

时间: 2024-10-25 08:43:52

POJ 1187 陨石的秘密 (线性DP)的相关文章

POJ 2355 Railway tickets (线性dp)

OJ题目 :click here~ 题目分析:X为距离 , 当0<X<=L1, 票价为C1. L1<X<=L2 ,票价为C2.L2<X<=L3,票价为C3.给每段车站时间的距离,求某两个车站之间的总票价的最小值. 设dp[ i ] 为到车站 i 的最少票价 . 则转移方程为dp[ i ] = min(dp[ j ] + 从j 到 i 的票价),j 为所有可以直接到 i 的车站. 要注意第一个数字 大于 第二个数字的情况.的确,题目没有说,从a 到 b.只说了a,b之间.

poj 1050 To the Max(线性dp)

题目链接:http://poj.org/problem?id=1050 思路分析: 该题目为经典的最大子矩阵和问题,属于线性dp问题:最大子矩阵为最大连续子段和的推广情况,最大连续子段和为一维问题,而最大子矩阵为二维问题, 可以考虑将二维问题转换为一维问题,即变为最大子段和问题即可求解: 先考虑暴力解法,暴力解法需要枚举子矩阵的左上角元素的坐标与子矩阵的右下角坐标即可枚举所有的子矩阵:对于每个子矩阵,考虑压缩子矩阵的每一列 元素,即求每一列的元素的和,这样子矩阵就转换为一维的情况,再使用最大子段

POJ 1958 Strange Towers of Hanoi (线性dp,记忆化搜索)

JQuery工具方法. (1)$.isNumeric(obj) 此方法判断传入的对象是否是一个数字或者可以转换为数字. isNumeric: function( obj ) { // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...&

POJ 1958 Strange Towers of Hanoi (四塔问题,线性dp,记忆化搜索)

题目分析:四柱汉诺塔.由于题目已经给出了求解方法,直接写代码即可.下面总结一下,四塔问题. 感谢这篇文章的作者,点这里就到,总结的很好.直接贴过来~ 四塔问题:设有A,B,C,D四个柱子(有时称塔),在A柱上有由小到大堆放的n个盘子. 今将A柱上的盘子移动到D柱上去.可以利用B,C柱作为工作栈用,移动的规则如下: ①每次只能移动一个盘子. ②在移动的过程中,小盘子只能放到大盘子的上面. 设计并实现一个求解四塔问题的动态规划算法,并分析时间和空间复杂性. 算法思想: 用如下算法移动盘子(记为Fou

POJ 1163 The Triangle (简单线性dp)

OJ题目 : click here~~ 题目分析:给一个数字三角形,从最上面一个数字开始,方向只能往左下或者右下,一直到最后一行,求经过的所有数字和的最大值. 搞清楚在输入的数据中,route的方向就行. AC_CODE int num[102][102]; int main(){ int n , i , j , k ; while(cin >> n){ int x[102][102]; for(i = 1;i <= n;i++) for(j = 1;j <= i;j++) sca

poj 1088 滑雪(线性DP)

滑雪 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 81553   Accepted: 30437 Description Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道载一个区域中最长底滑坡.区域由一个二维数组给出.数组的每个数字代表点的高度.下面是一个例子 1 2 3 4 5 16 17

POJ 1157 LITTLE SHOP OF FLOWERS (线性dp)

OJ题目:click here~~ 题目分析:f个束花,编号为1-- f.v个花瓶,编号为1 -- v.编号小的花束,所选花瓶的编号也必须比编号大的花束所选花瓶的编号小,即花i 选k, 花j选t ,如果i < j ,则定有k < t . 如果 i > j , 则定有 k > t . 每束花放在每个花瓶里有一个值.求f束花,能得到的最大值. 设dp[ i ][ j ] 为第 i 束花选择了第 j 个花瓶 , 则转移方程为 dp[ i ][ j ] =  max(dp[ i  - 1]

POJ 2479-Maximum sum(线性dp)

Maximum sum Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 33918   Accepted: 10504 Description Given a set of n integers: A={a1, a2,..., an}, we define a function d(A) as below: Your task is to calculate d(A). Input The input consists o

uva 11584 Partitioning by Palindromes 线性dp

// uva 11584 Partitioning by Palindromes 线性dp // // 题目意思是将一个字符串划分成尽量少的回文串 // // f[i]表示前i个字符能化成最少的回文串的数目 // // f[i] = min(f[i],f[j-1] + 1(j到i是回文串)) // // 这道题还是挺简单的,继续练 #include <algorithm> #include <bitset> #include <cassert> #include <