ZOJ 3791 An Easy Game DP

An Easy Game

Time Limit: 2 Seconds      Memory Limit: 65536 KB

One day, Edward and Flandre play a game. Flandre will show two 01-strings s1 and s2, the lengths of two strings are n. Then, Edward must move exact k steps. In each step, Edward should change exact m positions of s1. That means exact m positions of s1, '0' will be changed to '1' and '1' will be changed to '0'.

The problem comes, how many different ways can Edward change s1 to s2 after k steps? Please calculate the number of the ways mod 1000000009.

Input

Input will consist of multiple test cases and each case will consist of three lines. The first line of each case consist of three integers n (1 ≤ n ≤ 100), k (0 ≤ k ≤ 100), m (0 ≤ mn). The second line of each case is a 01-string s1. The third line of each case is a 01-string s2.

Output

For each test case, you should output a line consist of the result.

Sample Input
3 2 1
100
001
Sample Output
2
Hint
100->101->001
100->000->001

Author: CHEN, Zemin

Source: ZOJ Monthly, June 2014

题目链接:ZOJ 3791 An Easy Game

题目大意:

给你两个长度为n的01串,第一个为起始串,另一个为目标串。给你k次操作,每次选择起始串的m个不同的位置取反(0变1,1变0),问k次操作以后有多少种方案能使得起始串变成目标串。

题目分析:

其实这个串上1和0的分布无所谓的,只要看起始串和目标串在没有任何操作前有多少位是不同的就行。

设dp[i][j]表示第i次操作起始串与目标串还有j个位置不匹配。

那么状态转移方程为dp[i + 1][j + m - 2 * i2] = (dp[i + 1][j + m - 2 * i2] % mod + C[j][i2] * C[n - j][m - i2] % mod * dp[i][j] % mod)%mod。

其中C[j][i2] * C[n - j][m - i2]表示从从匹配的位置中选了i2个,不匹配的位置中选了m - i2个的组合数,dp[i + 1][j + m - 2 * i2]就是这次操作以后与目标串的不匹配的数目变为了j + m - 2 * i2个。

PS:一开始老是想着怎么从之前的状态推过来,然后怎么写也写不出来,反正就是觉得蛮复杂的,可能是我太渣的缘故,反正换成从当前状态推之后的状态就容易多了,有兴趣的筒子可以尝试从之前的状态推过来,反正我是弃疗了。。

更加可悲的是取模写错导致蛙了几发,以前遇到的都是1e9 + 7的,这次直接脑不带的就这么取了。。没治了没治了

//ZOJ 3791 An Easy Game
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X, SIZE) memset(A, X, sizeof(A[0]) * (SIZE))
#define copy(A, B) memcpy(A, B, sizeof A)
#define min(A, B) ((A) < (B) ? (A) : (B))
#define max(A, B) ((A) > (B) ? (A) : (B))
typedef long long ll;
const int maxE = 1000000;
const int maxN = 105;
const int oo = 0x3f3f3f3f;
const int mod = 1e9 + 9;
ll C[maxN][maxN], dp[maxN][maxN];
char s1[maxN], s2[maxN];
int n, m, k;
void fun(){
    for(int i = 0; i < maxN; ++i) C[i][0] = 1;
    for(int i = 1; i < maxN; ++i){
        for(int j = 1; j <= i; ++j){
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
}
void work(){
    int dif = 0;
    scanf("%s%s", s1, s2);
    for(int i = 0; i < n; ++i) if(s1[i] != s2[i]) ++dif;
    memset(dp, 0, sizeof dp);
    dp[0][dif] = 1;
    for(int i = 0; i < k; ++i){
        for(int j = 0; j <= n; ++j){
            for(int i2 = 0; i2 <= m; ++i2){
                if(j + m - 2 * i2 < 0) break;
                if(j + m - 2 * i2 > n) continue;
                dp[i + 1][j + m - 2 * i2] = (dp[i + 1][j + m - 2 * i2] % mod + C[j][i2] * C[n - j][m - i2] % mod * dp[i][j] % mod) % mod;
            }
        }
    }
    printf("%lld\n", dp[k][0]);
}
int main(){
    fun();
    while(~scanf("%d%d%d", &n, &k, &m)) work();
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值