您的位置:首页 > 大数据 > 人工智能

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  codeforces 算法 代码 dp