zoukankan      html  css  js  c++  java
  • 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
    ", (dp[n][m][t][d] - dp[n][m][t][d - 1] + mod ) % mod);
    		} else {
    			printf("%d
    ", dp[n][m][t][d]);
    		}
    	}
    }
     
    

      

  • 相关阅读:
    为什么你不是优秀的人?是这个原因么?
    我们应选择怎样的IT公司
    如何获得加薪
    隐藏为了适时出现
    如何通过一个问题,完成最成功的技术面试
    阿里负责人揭秘面试潜规则
    应聘互联网公司的简历应该是怎么样的?
    linq 图解
    Lambda表达式的前世今生
    Lambda应用设计模式
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10339523.html
Copyright © 2011-2022 走看看