传送门: http://codeforces.com/gym/101028/problem/J
【暑期训练正式开始了,加油~~争取区域赛资格,打破去年拿铁的悲剧(>﹏<),希望这个博客也能见证自己的一步步成长】
题意:在一串数中选择出严格递增的子序列,并且使得该数的2的多少幂次之和最小
思路:dp[i][j]表示检索到第i个数时其中最大的数是a[j]时候的取值之和。 如果a[j]<a[i] dp[i][i]=max(dp[i][i],dp[i][j]+foo(a[i])
思路很简单,但是遇到两个大坑点!!(其实就是自己dp没有学好 ==)
j的循环范围是从0开始的,因为不确定是否会取得第一个值,比如dp[3][0]的意思是1,2都不取,从3开始决定取不取,如果而我写的是从1开始也就会自动选取第一个数,15 14可以验证。 (不仔细)
对于当a[j]>=a[i]的时候一定是有dp[i][j]=dp[i-1][j]的,但是考虑到在循环中其实j<i恒成立,那么对于dp[i][i]没有影响,但是如果在普通情况下没有考虑到的话就会因为值没能传递下去而出错 ,通过4 13 8可以验证 在dp[2][1]的时候因为没有传递下去就是0,明显dp[2][1]是通过dp[1][1]传递过来的,因此就是需要将值进行传递的。
感谢师兄的教导和帮忙仔细查错,想想去年没好好训练辜负了师兄的期望真是很对不起。。。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[110][110];
int a[110];
int foo(int n)
{
int cnt=0;
while(n%2==0)
{
n/=2;cnt++;
}
return cnt;
}
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
dp[i][j]=dp[i-1][j];
//if(a[j]>=a[i])
// dp[i][j]=dp[i-1][j];
else dp[i][i]=max(dp[i-1][j]+foo(a[i]),dp[i][i]);
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,dp[n][i]);
printf("%d\n",ans);
}
return 0;
}
#include <cstdio>#include <algorithm>#include <cstring>using namespace std;int dp[110][110];int a[110];int foo(int n){ int cnt=0; while(n%2==0) { n/=2;cnt++; } return cnt;}int main(){ int t,n; scanf("%d",&t); while(t--) { memset(dp,0,sizeof(dp)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { for(int j=0;j<i;j++) { dp[i][j]=dp[i-1][j]; if(a[j]>=a[i]) dp[i][j]=dp[i-1][j]; else dp[i][i]=max(dp[i-1][j]+foo(a[i]),dp[i][i]); } } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dp[n][i]); printf("%d\n",ans); } return 0;}