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]的情况。当相等时,我们的决策有两种:把它放进之前的一段中或者把它单独分一个段。把它单独分一个段的方案总数为Σn−1i=1f[i][m−1][k−1] ,把它合并进之前的一段的方案总数为f[n−1][m−1][k] 。
如果不同,那么方案数为0。因为根据定义,它根本无法匹配。
= =Orz参考一下别人的:
https://blog.sengxian.com/solutions/noip-2015-day2然后用一个s数组来保存
Σn−1i=1f[i][m−1][k−1] 。最后用滚动数组(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)!
- NOIP 2015 Senior 5
- NOIP 2015 Senior 2
- NOIP 2015 Senior 3
- NOIP 2015 Senior 4
- NOIP 2015 Senior 6
- NOIP 2011 Senior 5
- NOIP 2012 Senior 5
- NOIP 2014 Senior 5
- NOIP 2013 Senior 5
- NOIP 2016 Senior 5
- NOIP 2017 Senior 5
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2009 Senior 3
- NOIP 2011 Senior 2
- NOIP 2011 Senior 3
- NOIP 2011 Senior 4
- NOIP 2011 Senior 6
- mysql基本sql语句总结(一)
- 机器学习——贝叶斯朴素贝叶斯 知识点与面试总结
- caffe accuracy_layer.cpp 解读
- win7和Ubuntu双系统卸载Ubuntu
- vector,set,map,list,deque的区别与联系
- NOIP 2015 Senior 5
- darknet 验证集的使用
- 前端学习笔记-SASS的使用
- 今天留的作业题。自己写的。
- 深入理解Linux内核 第三章笔记
- CSS基础知识(一)
- mainLoop
- Linux cifs vfs: send error in read = -11 cp大文件出错
- android版本与linux内核版本对应关系