NOIP 2015 Senior 5

来源:互联网 发布:windows 10系统还原 编辑:程序博客网 时间:2024/05/22 00:42

题目

思路1 爆搜
这道题其实我也只知道爆搜了= =。其实这道题运气很好,爆搜能骗个50分。在此还是贴个代码,给自己留个告诫,告诫自己一定要把代码写严谨些Orz

int f[505][55][55]; //Orzint solve(int fromA, int fromB, int cases = k){    if(f[fromA][fromB][cases] != -1) return f[fromA][fromB][cases];    int ans = 0;    if(n - fromA < cases || m - fromB < cases) return 0;    if(cases == 1)    {        for(int i = fromA, j = 1; i + m - fromB - 1 < n; i++, j++)        {            if(strncmp(&A[i], &B[fromB], m - fromB) == 0) ans++;        }        return f[fromA][fromB][cases] = ans;    }    for(int i = fromB, j = 1; i < m; i++, j++)    {        for(int l = fromA; l + j - 1 < n; l++)        {            if(strncmp(&A[l], &B[fromB], j) == 0)            {                ans += solve(l + j, fromB + j, cases - 1);                ans %= mod;            }        }    }    return f[fromA][fromB][cases] = ans;}

思路2 动态规划
还是要多跟大犇学学DP Orz。下面说说这道题动规的推导思路。

题目给了我们什么?n,m,k。 于是我们可以定义状态为f[n0][m0][k0],含义就是在A串的前n0个字符中找到B串的前m0个字符,且第A串的第n0个字符正好与B串的第m0个字符匹配,B串恰好分成k0段的方案数!!!(所以这是递推)
当k = 1时,你会想到什么?没错,公共子序列!于是我们的决策分两种情况:A[n] = B[m]的情况和A[n] != B[m]的情况。当相等时,我们的决策有两种:把它放进之前的一段中或者把它单独分一个段。把它单独分一个段的方案总数为Σn1i=1f[i][m1][k1],把它合并进之前的一段的方案总数为f[n1][m1][k]
如果不同,那么方案数为0。因为根据定义,它根本无法匹配。
= =Orz参考一下别人的:
https://blog.sengxian.com/solutions/noip-2015-day2

然后用一个s数组来保存Σn1i=1f[i][m1][k1]。最后用滚动数组(Orz)优化空间(否则只有70分,开大了就只有0分)

参考代码

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){    int a;    scanf("%d",&a);    return a;}const int mod = 1000000007;const int maxn = 1005;const int maxm = 205;int n, m, k;char A[maxn];char B[maxm];int f[2][maxm][maxm];int s[2][maxm][maxm];void run(){    n = readIn();    m = readIn();    k = readIn();    scanf("%s%s", A + 1, B + 1);    s[0][0][0] = 1;    for(int i = 1; i <= n; i++)    {        memset(s[i % 2], 0, sizeof(s[i % 2]));        memset(f[i % 2], 0, sizeof(f[i % 2]));        s[i % 2][0][0] = 1;        for(int j = 1; j <= m; j++)        {            if(A[i] == B[j])            {                for(int c = 1; c <= std::min(j, k); c++)                {                    f[i % 2][j][c] = (f[(i + 1) % 2][j - 1][c] + s[(i + 1) % 2][j - 1][c - 1]) % mod;                    s[i % 2][j][c] = (s[(i + 1) % 2][j][c] + f[i % 2][j][c]) % mod;                }            }            else            {                for(int c = 1; c <= std::min(j, k); c++)                {                    s[i % 2][j][c] = s[(i + 1) % 2][j][c];                }            }        }    }    printf("%d\n", s[n % 2][m][k]);}int main(){    run();    return 0;}

关于递推(DP):首先,需要准确地描述状态。其次,需要找到正确的决策。决策的要求是:划分成的情况的集合并起来等于全集,并且没有交集。最后,需要看看能否用滚动数组进行优化。一定要检查是否有无效的数据没有更改(有的话使用memset)!

原创粉丝点击