| Time Limit: 1000MS | Memory Limit: 30000K |
Description
"Buy low; buy lower"
Input
Output
Sample Input
12
68 69 54 64 68 64 70 67 78 62
Sample Output
4 2
Source
ps: 这道题的两个问题都可以用动态规划来求解
| 1.求最长的递减序列的长度 |
| 2.求最长递减序列的种类(不能重复) |
dp_len[i] = max(dp_len[i], dp_len[j] + 1);
dp_len[i]表示已a[ i ] 结尾的最长递减序列的长度。
第二个问题:去重是重中之重。比如一个序列5,4,6,4,输出应为2 2,因为只有序列(5,4)和(6,4)符合,注意其中(5,4)出现了两次。我们用dp_cnt[i]来表示以 a[i] 结尾长度为 dp_len[i]的种类。那么dp_cnt[i]应该等于多少呢?
举个粟子:
| i | 1 | 2 | 3 | 4 | 5 | 6 |
| a[i] | 10 | 8 | 5 | 9 | 5 | 2 |
| dp_len[i] | 1 | 2 | 3 | 2 | 3 | 4 |
| dp_cnt[i] | 1 | 1 | 1 | 1 | 2 | 2 |
可以看出dp_cnt[i] = sum(dp_cnt[j]), 0 <= j < i, dp_len[j] + 1 = dp_len[i], 比如dp_cnt[5] = dp_cnt[2] + dp_cnt[4] = 2;也就是以5结尾的符合序列为(10,8,5)和(10, 9, 5)。
接下来就是去重的选择了,比如对于2结尾的序列(10,8,5,2)出现了两次,那么2的前驱5的选择是任意的吗?答案当然不是,如上表,一个dp_cnt[3] = 1, 另一个dp_cnt[5] = 2,显然后者的选择多些,因此求dp_cnt[i]的过程j的枚举从后面开始,如何判断重复呢?当dp_len[i] == dp_len[j] && a[i] == a[j]时必然重复。
#include<iostream>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
int a[5004];
int dp_len[5004];
int dp_cnt[5004];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
while(cin>>n)
{
for(int i = 1; i <= n; i++)cin>>a[i];
a[0] = INT_MAX;
memset(dp_len, 0, sizeof dp_len);
memset(dp_cnt, 0, sizeof dp_cnt);
int ans_len = -1, ans_cnt = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < i; j++)
{
if(a[i] < a[j])
{
dp_len[i] = max(dp_len[j] + 1, dp_len[i]);
}
}
if(ans_len < dp_len[i]){
ans_len = dp_len[i];
}
}
dp_cnt[0] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = i - 1; j >= 0; j--)
{
if(dp_len[i] == dp_len[j] && a[i] == a[j]){
break;
}
if(dp_len[i] == dp_len[j] + 1 && a[i] < a[j])
{
dp_cnt[i] += dp_cnt[j];
}
}
if(dp_len[i] == ans_len) ans_cnt += dp_cnt[i];
}
cout<<ans_len<<" "<<ans_cnt<<endl;
}
return 0;
}

4299

被折叠的 条评论
为什么被折叠?



