zoukankan      html  css  js  c++  java
  • 【BZOJ4321】queue2【dp】

    【BZOJ4321】queue2【dp】

    题目描述

    (n) 个沙茶,被编号 (1~n)。排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 (1)(+1)(-1))就行;

    现在想知道,存在多少方案满足沙茶们如此不苛刻的条件。

    输入格式

    只有一行且为用空格隔开的一个正整数 (N)

    输出格式

    一个非负整数,表示方案数对 7777777 取模。

    样例

    样例输入

    4
    

    样例输出

    2
    

    样例解释

    有两种方案 (2 4 1 3)(3 1 4 2)

    数据范围与提示

    对于(30\%)的数据满足(Nleqslant 20)

    对于(100\%)的数据满足 ;(1leqslant N leqslant 10^4)

    分析

    这种带有这种限制的 (dp) 题,我们通常的状态定义就是前 (i)(j) 结尾的方案数是多少,但是这个题并不能这样写,因为下一个放置的人的编号会影响当前放置编号后的状态,所以我们换一个思路:

    定义 (f[i][j][0/1]) 表示向序列中插入元素 (i) ,当前有 (j) 对两两之间相差一的人, (0/1)表示当前插入的 (i) 是否和 (i-1)挨着,那么由于第三维的不确定性,我们需要考虑两个状态转移。

    首先看 (f[i][j][1]) 的转移,因为 (f[i][j][1]) 表示当前插入 (i) 后有 (j) 对编号相差一的,因为这里表示插入后 (i)(i-1) 是相邻的,所以只可能从 (j-1) 对或者 (j) 对转移来。当 (i-1)(i-2) 相邻的时候,也就是第三维是 (1) ,这时候会有两种能转移来的方案:

    一个是从 (f[i-1][j][1]) 转移来,这时候我们只需要把 (i)插入到 (i-1)(i-2) 中间就行。

    还有一个是从 (f[i-1][j-1][1]) 转移来,我们只需要在 (i-1) 不和 (i-2) 挨着的那一侧插入 (i) 即可。

    然后考虑第三维是 (0) ,这时候只可能从 (f[i-1][j-1][0]) 转移来,因为要求 (i)(i-1) 必须相邻,这时候 (i-1)(i-2) 是不相邻的,所以我们有两种插入方法,在 (i-1) 左侧和右侧。那么这个转移方程就是:

    [f[i][j][1] = f[i-1][j][1] + 2 imes f[i-1][j-1][0] + f[i-1][j-1][1] ]

    然后就是 (f[i][j][0]) 的转移,当前如果 (i)(i-1) 不相邻,那么只可能让 (j) 不变或者减小,所以上一个的 (j) 只可能是 (j)(j+1)

    首先考虑从 (f[i-1][j+1][0]) 的转移 这时候只需要在 (j+1) 段中任意插入个 (i) 就成了 (f[i][j][0]) ,而这时候有 (j+1) 种,也就是 (f[i-1][j+1][0] imes (j+1))

    然后是 (f[i-1][j+1][1]) 这时候不能插入到 (i-1)(i-2) 的这一段中,所以情况有 (j) 种,也就是 (f[i-1][j+1][1] imes j)

    然后分析上一次也有 (j) 对的情况,如果挨着,即(f[i-1][j][1]),那么可以在除了这些对之外的位置放,一共有 (i-j-2) 种,因为也可以在 (i-1)(i-2) 之间放,所以有 (i-j-1) 种,不挨着的情况与这个类似,只不过只能在这些对之外的地方放,那么状态转移方程就是:

    [f[i][j][0] = f[i-1][j+1][0] imes (j+1) + f[i-1][j+1][1] imes j + f[i-1][j][1] imes (i-j-1) + f[i-1][j][0] imes (i-j-2) ]

    代码

    #include<cstdio>
    using namespace std;
    #define ll long long
    const int maxn = 1000+10;
    const int mod = 7777777;
    ll f[maxn][maxn][2];
    int main(){
    	int n;
    	scanf("%d",&n);
    	f[2][1][1] = 2;
    	for(int i=3;i<=n;++i){
    		for(int j=0;j<i;++j){
    			f[i][j][0] = (f[i-1][j+1][0] * (j+1) + f[i-1][j+1][1] * j + f[i-1][j][1] * (i-j-1) + f[i-1][j][0] * (i-j-2))%mod;
    			if(j)f[i][j][1] = (f[i-1][j][1] + 2 * f[i-1][j-1][0] + f[i-1][j-1][1])%mod;
    		}
    	}
    	printf("%lld
    ",f[n][0][0]);
    	return 0;
    }
    
  • 相关阅读:
    new对象数组时的内存布局
    写程序取自己进程的AEP
    类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)
    测试 __try, __finally, __except(被__finally捕获的异常, 还会被上一级的__except捕获。反之不行)
    围观M$的new
    将258.369 double值转为内存表示(科学计数法)
    Broadcast Reveiver作用
    DEBUG模式下, 内存中的变量地址分析
    不包含SDK头文件, 补全API定义
    俄罗斯方块SDK版
  • 原文地址:https://www.cnblogs.com/Vocanda/p/13488490.html
Copyright © 2011-2022 走看看