给出长度为n的序列,问这个序列中有多少个长度为m的单调递增子序列。
dp[i][j],表示到第i个数字,长度为j的单调递增子序列的个数。需要注意的是取第j个数字
思路:通过枚举第i个数字来找出第i个数字前面存在的小于他的dp[k][i-1] 的数量 (i,j的位置不要颠倒了)
超时算法:
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int dp[1010][1010];
int a[1010];
int main()
{
int n,t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
dp[i][j]=0;
dp[0][0]=1;
for(int i=1;i<=m;i++)
{
for(int j=i;j<=n-m+2;j++)
{
for(int k=i-1;k<j;k++)
{
if(a[j]>a[k])
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
sum=(sum+dp[m][i])%mod;
}
printf("Case #%d: %d\n",cas,sum);
}
return 0;
}
树状数组将n^3优化成n^2logn
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define bug puts("********************")
#define LL long long
#define mod 1000000007
using namespace std;
LL dp[1100][1011];
int a[1010],b[1010];
int n,m;
int init(int x)
{
return x&(-x);
}
void update(int x,int y, int val)
{
while(x<=n)
{
dp[x][y]=(dp[x][y]+val)%mod;
x+=init(x);
}
}
LL getsum(int x,int y)
{
LL sum=0;
while(x>=1)
{
sum=(sum+dp[x][y])%mod;
x-=init(x);
}
return sum;
}
int main()
{
int t;
scanf("%d",&t);
for(int cas=1; cas<=t; cas++)
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{ ///如: 5,3,1,可以离散化处理之后为 3,2,1
b[i]=lower_bound(a+1,a+n+1,b[i])-(a); /// 离散化,将一个值映射到一个在集合中大小相对位置不变的较小的值
}
memset(dp,0,sizeof(dp));
LL sum=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=min(i,m);j++)
{//bug;
if(j==1)
update(b[i],1,1);
else
{
sum=getsum(b[i]-1,j-1);
update(b[i],j,sum);
}
}
}
printf("Case #%d: %lld\n",cas,getsum(n,m));
}
return 0;
}