zoukankan      html  css  js  c++  java
  • BZOJ4321queue2——DP/递推

    题目描述

    n 个沙茶,被编号 1~n。排完队之后,每个沙茶希望,自己的相邻的两
    人只要无一个人的编号和自己的编号相差为 1(+1 或-1)就行; 
    现在想知道,存在多少方案满足沙茶们如此不苛刻的条件。 

    输入

    只有一行且为用空格隔开的一个正整数 N,其中 100%的数据满足 1≤N ≤ 1000; 

    输出

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

    样例输入

    4

    样例输出

    2
    样例解释:有两种方案 2 4 1 3 和 3 1 4 2
     
    递推方法比较高深,据说与容斥有关:$f_{i}=(i+1)f_{i-1}-(i-2)f_{i-2}-(i-5)f_{i-3}+(i-3)f_{i-4}$
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<bitset>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    #define mod 7777777
    using namespace std;
    int n;
    ll f[1010];
    int main()
    {
    	scanf("%d",&n);
    	f[0]=f[1]=1ll;
    	f[2]=f[3]=0ll;
    	for(int i=4;i<=n;i++)
    	{
    		f[i]=1ll*f[i-1]*(i+1)%mod-1ll*f[i-2]*(i-2)%mod-1ll*f[i-3]*(i-5)%mod+1ll*f[i-4]*(i-3)%mod;
    		f[i]=(f[i]%mod+mod)%mod;
    	}
    	printf("%lld",f[n]);
    }
    

    $DP$做法:考虑对于前$i$个数的排列,当加入$i+1$时对排列的影响,设$f[i][j]/g[i][j]$分别表示前$i$个数的排列中有$j$对相差为$1$的数相邻(后文称为不合法数对)且$i$两边有/没有与它相差为1的的数。

    考虑这两个方程如何转移,对于$f[i][j]$,加入$i+1$:

    1、与$i$相邻且增加一对不合法数对,有一种放法,可转移到$f[i+1][j+1]$

    2、与$i$相邻且不合法数对数不变,有一种放法,可转移到$f[i+1][j]$

    3、与$i$不相邻且减少一对不合法数对,有$(j-1)$种放法,可转移到$g[i+1][j-1]$

    4、与$i$不相邻且不合法数对数不变,有$(i-j)$种放法,可转移到$g[i+1][j]$

    对于$g[i][j]$,加入$i+1$:

    1、与$i$相邻且增加一对不合法数对,有两种放法,可转移到$f[i+1][j+1]$

    2、与$i$不相邻且减少一对不合法数对,有$j$种放法,可转移到$g[i+1][j-1]$

    3、与$i$不相邻且不合法数对数不变,有$(i-j-1)$种放法,可转移到$g[i+1][j]$

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<bitset>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    #define mod 7777777
    using namespace std;
    int n;
    ll f[1010][1010];
    ll g[1010][1010];
    int main()
    {
    	scanf("%d",&n);
    	g[1][0]=1;
    	for(int i=1;i<n;i++)
    	{
    		for(int j=0;j<i;j++)
    		{
    			(f[i+1][j+1]+=f[i][j])%=mod;
    			(f[i+1][j]+=f[i][j])%=mod;
    			(g[i+1][j-1]+=1ll*(j-1)*f[i][j])%=mod;
    			(g[i+1][j]+=1ll*(i-j)*f[i][j])%=mod;
    			(f[i+1][j+1]+=2*g[i][j])%=mod;
    			(g[i+1][j-1]+=1ll*j*g[i][j])%=mod;
    			(g[i+1][j]+=1ll*(i-j-1)*g[i][j])%=mod;
    		}
    	}
    	printf("%lld",g[n][0]);
    }
  • 相关阅读:
    VC++实现感染文件式加载DLL文件
    vC++实现遍历桌面和快速启动里的所有快捷方式,判断快捷方式是不是浏览器,如果是则删除快捷方式参数
    VC++另类实现进程插入
    云服务系列:Windows Azure SDK for .NET(2012 年 6 月发布的版本)的最新消息
    VC++1.5K字节实现下载并远程注入
    上海求职指南 (最新版)
    WinAPI: GetWindowText 获取窗口标题
    WinAPI: SetCursorPos 设置鼠标指针位置
    WinAPI: SetComputerName 更改计算机名称
    GDI+ 学习记录(31): 图像颜色变换(TGPImageAttributes)
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10396345.html
Copyright © 2011-2022 走看看