Codeforces Round #323 (Div. 2) D.Once Again...(582B)
2015-10-10 20:19
363 查看
题意
给出一个长度为n(1<= n <= 100)的序列,使其重复t(1<= t <= 1e7)次后,求最长不减子序列的长度。思路
序列重复t次后总长度高达1e9,如果暴力dp,即使是O(n)的算法也不可取。因此需要另辟蹊径。长度为n的最长上升子序列可以容易由dp求得。重复t次可以分解为:一次的增加长度为n的序列,共增加t次。如果我们能够由两个长度为n的原序列的上升子序列得到一个长度为2*n的上升子序列,那么由于归纳的思想,可以求得长度为t*n的上升子序列的长度。原来单纯求解上升子序列长度的一维状态已经不够用,扩展为二维的状态,dp[i][j]表示以s[j]为结尾的且起始数字大于等于s[i]的最长上升子序列的长度。这样我们就可以利用这个dp数组来经过t次运算得到长度为n*t的最长上升子序列的长度。
运算方式即为dp转移方程。
dp[i][j] = max(dp[i][j],dp[i][k] + dp[k][j]) (0< k < n),ps:之所以dp状态中一维用来表示上升子序列的起点取值范围,就是为了能够满足上面提到的归纳思想的递推。
注意:在递推的中间状态中,比如dp[i][j] ^ k 中的某一点dp[a],表示以[b]最后n个数的s[b]结尾的且起始点大于等于s[i]的最长上升子序列。
同样的由于运算满足结合律,利用矩阵快速幂加速转移。时间复杂度:O(n^3*log(t))
个人注意
1:在求取dp[i][j],即初始状态的时候,注意与一维dp类比。在枚举第j位之前的情况之前,注意,初始化当前位的所有情况,即dp[k][j]。2:由于快速幂的基本运算变为求最大值运算,快速幂中的对角矩阵要改为0矩阵,表示初始为最小值,其他的运算也类似,需要相应的改变对角矩阵的定义。即dp[i][j]^0的定义。
代码
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n, ts; const int size = 200; const int maxn = 105; struct mat { long long v[105][105]; mat() { memset(v, 0, sizeof(v)); } }; int afx[size]; mat matrix_mul(mat p1, mat p2) { mat t; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if(afx[i] > afx[j]) continue; for(int k = 0; k < n; k++) { if(afx[i] > afx[k] || afx[k] > afx[j]) continue; t.v[i][j] = max(t.v[i][j], p1.v[i][k] + p2.v[k][j]); } } } return t; } mat matrix_mi(mat p, int k) { mat t; while(k) { if(k & 1) { // printf("yes\n"); t = matrix_mul(t, p); } k >>= 1; p = matrix_mul(p, p); } return t; } int main() { scanf("%d%d", &n, &ts); for(int i = 0; i < n; i ++) scanf("%d", afx + i); mat fs; for(int i = 0; i < n; i++) if(afx[i] <= afx[0]) fs.v[i][0] = 1; for(int j = 1; j < n; j++) { for(int k = 0; k < n; k++) if(afx[k] <= afx[j]) fs.v[k][j] = 1; for(int k = 0; k < j; k++) { if(afx[k] > afx[j]) continue; for(int m = 0; m < n; m++) { if(afx[m] > afx[k]) continue; fs.v[m][j] = max(fs.v[m][j], fs.v[m][k] + 1); } } } mat ans = matrix_mi(fs, ts); long long man = 0; for(int i = 0; i < n; i++) for(int j = 0; j < n ; j++) man = max(man, ans.v[i][j]); printf("%I64d\n", man); return 0; }
相关文章推荐
- android 代码实现控件之间的间距
- [Android]在代码里运行另一个程序的方法
- 肯特·贝克:改变人生的代码整理魔法
- 网页恶意代码的预防
- 动易2006序列号破解算法公布
- 高手写的Tracer-Flash代码调试类代码下载
- CSS代码缩写技巧
- 非主流Q-zOne代码代码搜集第1/2页
- Ruby实现的矩阵连乘算法
- CreateWeb.vbs 代码
- C#插入法排序算法实例分析
- Lua中编译执行代码相关的函数详解
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- 更有效率的css代码编写第1/3页
- 代码中到底应不应当写注释?