Codeforces 118D Caesar's Legions【dp】好题

探讨了在给定数量的步兵和骑兵以及连续排列的最大数量限制下,如何计算满足条件的不同排列方式总数的问题。

D. Caesar's Legions
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Gaius Julius Caesar, a famous general, loved to line up his soldiers. Overall the army had n1 footmen and n2 horsemen. Caesar thought that an arrangement is not beautiful if somewhere in the line there are strictly more that k1 footmen standing successively one after another, or there are strictly more than k2 horsemen standing successively one after another. Find the number of beautiful arrangements of the soldiers.

Note that all n1 + n2 warriors should be present at each arrangement. All footmen are considered indistinguishable among themselves. Similarly, all horsemen are considered indistinguishable among themselves.

Input

The only line contains four space-separated integers n1, n2, k1, k2 (1 ≤ n1, n2 ≤ 100, 1 ≤ k1, k2 ≤ 10) which represent how many footmen and horsemen there are and the largest acceptable number of footmen and horsemen standing in succession, correspondingly.

Output

Print the number of beautiful arrangements of the army modulo 100000000 (108). That is, print the number of such ways to line up the soldiers, that no more than k1 footmen stand successively, and no more than k2 horsemen stand successively.

Examples
Input
2 1 1 10
Output
1
Input
2 3 1 2
Output
5
Input
2 4 1 1
Output
0
Note

Let's mark a footman as 1, and a horseman as 2.

In the first sample the only beautiful line-up is: 121

In the second sample 5 beautiful line-ups exist: 12122, 12212, 21212, 21221, 22121


题目大意:

有n1个数字1,有n2个数字2(看的Note翻译的,不知道原文是不是也是这样)

将n1+n2个数字排成一排,其中要求连续的数字1最多可以有k1个,连续的数字2最多有k2个。

问一共有多少种分配方案。


思路:


1、统计计数问题,考虑dp。

设定dp【i】【j1】【j2】【k1】【k2】【l(2)】,表示dp到第i位,当前数字类型是l,其中数字1使用了j1个,数字2使用了j2个,并且最后一个数字(当前数字)之前的连续l(假设是0)一共有k1个,再之前连续的另外一种数字一共有k2个的方案数。明显能够包含住所有的情况,因为内存限制包括时间限制,我们考虑将这个数组降低维度。

我们其实只考虑当前数字的情况即可,那么:

设定dp【i】【j】【k】【l(2)】,表示当前dp到第i位,当前数字类型是l,对应当前数字l已经使用了j个,并且最后一个数字(当前数字)之前连续的l一共有k个的方案数。


2、设定好dp数组之后,考虑递推出其状态转移方程(此时我们l==0表示上边添加的数字为1,l==1表示上边天剑的数字为2):

if(k==1)
dp【i】【j】【k】【0】+=dp【i-1】【i-j】【kk】【1】;

表示我们当前第i位子上的数和第i-1位子上的数不同,那么对应我们需要考虑上一位子的状态,首先我们知道,在第i-1位子上的时候,一共有数字i-1个,对应其中一定包含j-1个数字1,那么其包含数字2的个数为:i-1-(j-1)=i-j;那么此时我们需要再枚举这样一个变量:因为我们此时第i-1位子上的数字要是2,那么我们需要知道此时连续的2的个数。那么我们再枚举一个kk即可。

if(k>1)

dp【i】【j】【k】【0】+=dp【i-1】【j-1】【k-1】【0】;

表示我们当前第i位子上的数和第i-1位子上的数相同,那么对应我们上一位子的状态:dp到第i-1位,已经使用数字1的个数为j-1个,对应连续的数字1的个数为k-1.直接累加上即可。

同理,对应有:

if(k==1)

dp【i】【j】【k】【1】+=dp【i-1】【i-j】【kk】【0】;

if(k>1)

dp【i】【j】【k】【1】+=dp【i-1】【j-1】【k-1】【1】;


3、注意对1e8取模,注意初始化,其他的就没有什么了。


Ac代码:

#include<stdio.h>
#include<string.h>
using namespace std;
#define mod 100000000
int dp[225][125][15][2];
int main()
{
    int n1,n2,k1,k2;
    while(~scanf("%d%d%d%d",&n1,&n2,&k1,&k2))
    {
        memset(dp,0,sizeof(dp));
        dp[1][1][1][0]=1;
        dp[1][1][1][1]=1;
        for(int i=2;i<=n1+n2;i++)
        {
            //////当前第i位子上的数字是0;
            for(int j=1;j<=i&&j<=n1;j++)
            {
                for(int k=1;k<=j&&k<=k1;k++)
                {
                    if(k==1)
                    {
                        for(int kk=1;kk<=k2;kk++)
                        {
                            dp[i][j][k][0]+=dp[i-1][i-j][kk][1];
                            dp[i][j][k][0]%=mod;
                        }
                    }
                    else
                    dp[i][j][k][0]+=dp[i-1][j-1][k-1][0];
                    dp[i][j][k][0]%=mod;
                }
            }
            //当前第i个位子上的数字是1
            for(int j=1;j<=i&&j<=n2;j++)
            {
                for(int k=1;k<=j&&k<=k2;k++)
                {
                    if(k==1)
                    {
                        for(int kk=1;kk<=k1;kk++)
                        {
                            dp[i][j][k][1]+=dp[i-1][i-j][kk][0];
                            dp[i][j][k][1]%=mod;
                        }
                    }
                    else
                    dp[i][j][k][1]+=dp[i-1][j-1][k-1][1];
                    dp[i][j][k][1]%=mod;
                }
            }
        }
        int output=0;
        for(int i=1;i<=k1;i++)
        {
            output+=dp[n1+n2][n1][i][0];
            output%=mod;
        }
        for(int i=1;i<=k2;i++)
        {
            output+=dp[n1+n2][n2][i][1];
            output%=mod;
        }
        printf("%d\n",output);
    }
}


### 筛选 Codeforces Div. 2 和 Div. 3 难度目的方法 在 Codeforces 上筛选适合训练的目是提升算法能力的重要一步。对于希望专注于 Div. 2 和 Div. 3 难度目的用户,可以通过以下方式进行操作。 Codeforces 的比赛通常分为 **Div. 1**、**Div. 2** 和 **Div. 3** 三类。其中,**Div. 3** 的目通常被认为是最适合初学者的,而 **Div. 2** 的目则适合有一定基础但仍需提升的选手。为了筛选这些目的练习资源,可以访问 Codeforces 的 [Problemset](https://codeforces.com/problemset) 页面。 在 Problemset 页面中,可以通过 URL 参数手动筛选特定比赛的目。例如,访问 `https://codeforces.com/problemset?tags=implementation` 可以查看标记为 "implementation" 的目,而 `https://codeforces.com/problemset?tags=math` 则可以查看与数学相关的目。此外,可以通过访问特定比赛的链接,例如 `https://codeforces.com/contest/1574/` 来查看某一场比赛的目列表 [^1]。 对于初学者,建议从 **Div. 3** 比赛中挑选目进行练习。例如,比赛编号为 `1574` 的比赛属于 **Div. 3** 类别,其中的目难度适中,适合巩固基础算法和数据结构知识 。此外,Codeforces 的博客功能也提供了许多用户分享的解思路和训练心得,可以帮助理解目的解法 [^3]。 在练习过程中,建议结合代码实现和目总结,避免只做不总结的情况。可以通过记录每次练习的思路和代码,逐步建立自己的解模板和方法库 。 #### 示例代码:构造一个简单的算法训练计划 ```python # 构建一个简单的每日训练计划 training_plan = { "Monday": "Sorting and Searching", "Tuesday": "Dynamic Programming", "Wednesday": "Graph Theory", "Thursday": "Math Problems", "Friday": "Implementation", "Saturday": "Virtual Contest", "Sunday": "Review and Summary" } for day, topic in training_plan.items(): print(f"{day}: {topic}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值