【递推DP+加深】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sxy201658506207/article/details/84668549

zoj 3747
题意:给n个士兵排队,每个士兵三种G、R、P可选,求至少有m个连续G士兵,最多有k个连续R士兵的排列的种数。

都转化为至多的士兵连续的个数。
令集合A={至多n个G士兵连续,且至多K个R士兵连续}
集合B={至多m-1个G士兵连续,且至多K个连续的R士兵连续}
C=A-B={至少m个士兵连续,且至少连续K个士兵连续}。

在转化要如何求 至多x个G士兵连续,至多y个士兵连续
dp[i][0]至多i个G 的方案数
dp[i][1]至多i个R 的方案数
dp[i][2]第i个为P 的方案数

对G士兵递推:
    当i<=x时
    dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
    当i=x+1时候
    dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1; 减去的是x+1位置为G士兵的时候
    当i>x+1的时候
    dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-x-1][1]-dp[i-x-1][2]; 此时要减去i前面已经出现连续u个G的情况,即从i-u到i-1这一段都是G,
那么i-1-u的位置可以是P或者R,
对士兵R递推:
同理
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
ll dp[maxn][4];
int n,m,k;
ll solve(int x,int y)//dp[i][0]至多i个G dp[i][1]至多i个R dp[i][2]第i个为P
{
    dp[0][0]=1;
    dp[0][1]=dp[0][2]=0;
    for(int i=1;i<=n;++i)
    {
        ll ans=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod;
        dp[i][2]=ans;
        if(i<=x)dp[i][0]=ans;
        if(i==x+1)dp[i][0]=(ans-1+mod)%mod;
        if(i>x+1)dp[i][0]=(ans-dp[i-x-1][1]-dp[i-x-1][2]+mod)%mod;

        if(i<=y)dp[i][1]=ans;
        if(i==y+1)dp[i][1]=(ans-1+mod)%mod;
        if(i>y+1)dp[i][1]=(ans-dp[i-x-1][0]-dp[i-x-1][2]+mod)%mod;
    }
    return (dp[n][0]+dp[n][1]+dp[n][2])%mod;
}
int main()
{

    while(cin>>n>>m>>k)
    {
        cout<<((solve(n,k)-solve(m-1,k))%mod+mod)%mod<<endl;
    }
    return 0;
}

uva10328  Coin Toss(计数问题)

就是抛硬币正面为H,反面为T,问抛n次的所有可能情况中连续H的个数至少为k的个数。

比起那个zoj3738这道题算是比较好想了,不过是同一种类型的题目,
dp[i][0]表示前i个正面朝上的方案总数,且第i个为正面朝上,且朝上的个数不超过k个
dp[i][1]为第i个位反面朝上,且前i个朝上的个数不超过k个
那么题目就求出最多n个朝上的总方案数-最多k-1个正面朝上的总方案数
不过和他的兄弟题目zoj 3738 在 dp[0][0]=1或者=0的时候我有点迷

另一个思路:链接
设f[i][j]表示抛第i个硬币时连续的H不超过j个的情况总数,那么由于第i个可以为正可以为负,因此不妨先写成f[i][j]=2*f[i-1][j]。
但是这么写有些情况下是不正确的,什么情况呢?就是前面i-1个硬币的最后j个全是H,那么第i个如果再是H的话,总长度就变成j+1了。
在减去这部分的种数的时候,我们要分情况讨论一下。如果i==j+1,那么就只有一种情况可以出现j+1个H,就是i个硬币全部是H,因此减去1即可。如果i>j+1,那么如果第i个硬币前面有j个H的话,那么第i-j-1个硬币必然应该是T,否则H的连续的数量就会超过j,这种情况就不可能包含在f[i-1][j]里面了,
    因此,我们这时只要减去一个f[i-j-2][j]即可。
    最后结果显然应该是输出f[n][n]-f[n][k-1]。

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
ll dp[maxn][2];
ll solve(int n,int k)
{
    memset(dp,0,sizeof(dp));
     dp[0][0]=0,dp[0][1]=1;
    for(int i=1;i<=n;++i)
    {
        ll ans=dp[i-1][0]+dp[i-1][1];
        dp[i][1]=ans;
        if(i<=k)dp[i][0]=ans;
        if(i==k+1) dp[i][0]=ans-1;
        if(i>k) dp[i][0]=ans-dp[i-k-1][1];
    }
    return dp[n][0]+dp[n][1];
}
int main()
{
    int n,k;
    while(cin>>n>>k)
    {
        cout<<solve(n,n)<<endl;
        cout<<solve(n,n)-solve(n,k-1)<<endl;
    }
    return 0;
}

HDU 4489
求n个人按照低高低或者高低高的顺序排列种数是多少
The king has guards of all different heights. Rather than line them up in increasing or decreasing height order, he wants to line them up so each guard is either shorter than the guards next to him or taller than the guards next to him (so the heights go up and down along the line). For example, seven guards of heights 160, 162, 164, 166, 168, 170 and 172 cm. could be arranged as:

or perhaps:

The king wants to know how many guards he needs so he can have a different up and down order at each changing of the guard for rest of his reign. To be able to do this, he needs to know for a given number of guards, n, how many different up and down orders there are:
For example, if there are four guards: 1, 2, 3,4 can be arrange as:
1324, 2143, 3142, 2314, 3412, 4231, 4132, 2413, 3241, 1423
     For this problem, you will write a program that takes as input a positive integer n, the number of guards and returns the number of up and down orders for n guards of differing heights.

Input
The first line of input contains a single integer P, (1 <= P <= 1000), which is the number of data sets that follow. Each data set consists of single line of input containing two integers. The first integer, D is the data set number. The second integer, n (1 <= n <= 20), is the number of guards of differing heights.
Output

For each data set there is one line of output. It contains the data set number (D) followed by a single space, followed by the number of up and down orders for the n guards.

Sample Input
4 1 1 2 3 3 4 4 20
Sample Output
1 1 2 4 3 10 4 740742376475050
 

参考:链接
题意:求n个人按照低高低或者高低高的顺序排列种数是多少
把n个人的身高设为1~n, 然后从低到高插入队列。 那么将第i个人插入队列的时候就出现了问题, 插入的这个位置需要满足前面两个是高低, 后面两个是低高。
所以我们用DP来记录。 用d[i][0] 表示i个人的队列, 结尾为高低的方法数, d[i][1]表示开头为低高的方法数。  那么假设将第i个人插入, 插入的位置前面有j个人, 后面有i - 1 - j个人, 那么当前方法数就累加d[j][0] * d[i-1-j][1] * c[i-1][j]。 最终求得的是有i个人的时候的所有方法数,

那么怎么递推DP数组d[i][1] 和 d[i][0] 呢,因为开始为低高和结尾为高低的方法数相同且各占总方法数的一半
证明:
当n为偶数时:假设高低开始的序列为1010.那么把它倒置一下就变成了0101了
也就是说每一个1打头的对应着一个0打头的

当n为奇数时:假设波峰开始的序列为10101.
假设第一个数大于最后一个数,那我们把序列最后一个数放到最前序列就变成01010.
如果第一个数小于最后一个数把第一个数放到最后就行了
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
ll dp[50][2];
ll c[50][50];
int pos,n;
void init()
{
    for(int i=1;i<=20;++i)
    {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;++j)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
    dp[0][1]=dp[0][0]=1;
    dp[1][0]=dp[1][1]=1;
    ll cur=0;
    for(int i=2;i<=20;++i)
    {
        cur=0;
        for(int j=0;j<i;++j)
            cur+=dp[j][0]*dp[i-j-1][1]*c[i-1][j];
        dp[i][1]=dp[i][0]=cur/2;
    }
}
int main()
{
    init();
    int t;
    cin>>t;
    while(t--)
    {
        cin>>pos>>n;
        if(n==1){ cout<<pos<<' '<<1<<endl;continue;}
        cout<<pos<<' '<<dp[n][0]+dp[n][1]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sxy201658506207/article/details/84668549