Codeforces Round #323 (Div. 2) Once Again... CodeForces - 582B 最长非下降子序列【dp】(不明白)

B. Once Again...
time limit per test

1 second

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given an array of positive integers a1, a2, ..., an × T of length n × T. We know that for any i > n it is true that ai = ai - n. Find the length of the longest non-decreasing sequence of the given array.

Input

The first line contains two space-separated integers: nT (1 ≤ n ≤ 100, 1 ≤ T ≤ 107). The second line contains n space-separated integers a1, a2, ..., an (1 ≤ ai ≤ 300).

Output

Print a single number — the length of a sought sequence.

Examples
input
4 3
3 1 4 2
output
5
Note

The array given in the sample looks like that: 3, 1, 4, 2, 3, 1, 4, 2, 3, 1, 4, 2. The elements in bold form the largest non-decreasing subsequence.

  其实想了好久都不明白网上有人说是插空是怎么回事,现在也不明白。先记下,看以后会不会懂。。。只能泛泛的理解长度为n的序列重复n次应该会出现比较特殊的情况,比如‘循环’什么的。在怎么循环这个重复的序列也只有n个不同的数。t比n大很多,t<=n时直接做不会超时,t很大时,只求到重复n次的lis。之后加上后面原来数列里面出现次数最多的数的次数*(t-n)。可以想象,一个数在原序列里面出现次数越多,最长不下降数列里面它出现的几率和次数应该也是最多的。

实现过程中用到upper_bound()函数,它是求一个数的上界位置,就是比要求的数大的第一个位置,注意这里是大于不是大于等于。比如a[5]={1,2,2,2,5},upper_bound(a,a+5,2)-a=4.(严格来说返回的是数组编号地址)就是比2大的第一个位置下标为4。所以当数组里所有元素都小于等于给定的key的话,它会返回数组最后一个元素的下一个位置的下标,即upper_bound(a,a+5,5)-a=5。而lower_bound()函数是求大于等于当前值的第一个位置,所以lower_bound(a,a+5,2)-a=1,即第一次出现大于等于2的位置下标为1。

非下降子序列和递增有一点的不同就是允许重复。所以用upper_bound()求当前元素的下一个位置,再更新。知道upper_bound()的作用代码就好理解了:

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + ;
int dp[MAXN];
int num[MAXN], a[];
int re[]; int main()
{
int n, t;
int maxncnt = ;
memset(re, , sizeof(re));
memset(dp, , sizeof(dp));
scanf("%d%d", &n, &t);
for (int i = ; i < n; i++) {
scanf("%d", &a[i]);
re[a[i]]++;
if (re[a[i]] > maxncnt) maxncnt = re[a[i]];
}
int cnt = ;
for (int i = ; i < min(n, t); i++) {
for (int j = ; j < n; j++)
num[cnt++] = a[j];
}
int val = ;
for (int i = ; i < cnt; i++) {
int pos = upper_bound(dp, dp + val, num[i]) - dp;
if (pos == val) dp[val++] = num[i];
else dp[pos] = num[i];
}
if (t > n) val += (t - n)*maxncnt;
printf("%d\n", val);
return ;
}

求非减lis处代码还可以仿照lis求法做下小改动。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + ;
int dp[MAXN];
int num[MAXN], a[];
int re[],val; int main()
{
int n, t;
int maxncnt = ;
memset(re, , sizeof(re));
memset(dp, , sizeof(dp));
scanf("%d%d", &n, &t);
for (int i = ; i < n; i++) {
scanf("%d", &a[i]);
re[a[i]]++;
if (re[a[i]] > maxncnt) maxncnt = re[a[i]];
}
int cnt = ;
for (int i = ; i < min(n, t); i++) {
for (int j = ; j < n; j++) {
num[cnt++] = a[j];
}
}
int val = ;
dp[] = num[];
for (int i = ; i < cnt; i++) {
if (num[i] >= dp[val - ]) dp[val++] = num[i];
else
{
int pos = upper_bound(dp, dp + val, num[i]) - dp;
dp[pos] = num[i];
}
}
if (t > n) val += (t - n)*maxncnt;
printf("%d\n", val);
return ;
}

我非作死想要自己实现类似upper_bounder()的作用自己写二分,怎么写都不对,但还是有人会写。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + ;
int dp[MAXN],num[MAXN];
int a[];
int re[], val; int binary_search(int i)
{
int left, right, mid;
left = , right = val;
while (left<right)
{
mid = (left + right) >> ;
if (dp[mid] > num[i]) right = mid;
else left = mid + ;
}
return left;
} int main()
{
int n, t;
int maxncnt = ;
memset(re, , sizeof(re));
memset(dp, , sizeof(dp));
scanf("%d%d", &n, &t);
for (int i = ; i <= n; i++) {
scanf("%d", &a[i]);
re[a[i]]++;
if (re[a[i]] > maxncnt) maxncnt = re[a[i]];
}
int cnt = ;
for (int i = ; i <= min(n, t); i++) {
for (int j = ; j <= n; j++)
num[cnt++] = a[j];
}
val = ;
dp[] = num[];
for (int i = ; i <= cnt; i++) {
if (num[i] >= dp[val])
dp[++val] = num[i];
else {
int pos = binary_search(i);
dp[pos] = num[i];
}
}
printf("%d\n", val + maxncnt*(t - min(n, t)));
return ;
}

另一种二分写法:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
int a[];
int s[];
int m[]; int main()
{
int n,T;
scanf("%d%d",&n,&T);
memset(m,,sizeof(m));
int ansm=;
for(int i=;i<n;i++){
scanf("%d",a+i);
m[a[i]]++;
ansm=max(m[a[i]],ansm);
}
int top=;
for(int i=;i<min(n,T);i++){
for(int j=;j<n;j++){
if(top==||a[j]>=s[top-]){
s[top]=a[j];
top++;
}else{
int l=,r=top-;
int ans=r;
while(l<=r){
int mid=(l+r)>>;
if(s[mid]>a[j]){
ans=mid;
r=mid-;
}else{
l=mid+;
}
}
s[ans]=a[j];
}
}
}
printf("%d\n",top+ansm*(T-min(n,T)));
return ;
}
上一篇:设计模式之单例模式C++实现


下一篇:DP Codeforces Round #303 (Div. 2) C. Woodcutters