南阳ccpc C题 The Battle of Chibi && hdu5542 The Battle of Chibi (树状数组优化+dp)
题意:
给你一个长度为n的数组,你需要从中找一个长度为m的严格上升子序列
问你最多能找到多少个
题解:
我们先对原序列从小到大排序,排序之后的序列就是一个上升序列
这里如果两个数相等的话,那么因为题目要我们求严格上升子序列,所以我们让这个数在数组中原来位置靠后的排序之后让它靠前(靠前也就是下标小)
我们dp方程:dp[i][j]表示截至到第i(这个i是按照没排序之前的下标)个元素,上升子序列长度为j的子序列能找到dp[i][j]个
dp转移方程:dp[i][j]=dp[1--i-1][j-1]
dp[1--i-1][j-1]就表示dp[1][j-1]+dp[2][j-1]+...+dp[i-1][j-1]
可以说就是求前缀和,这里用的是树状数组维护的
比如原序列为:3 11 5 2 6
排序后序列为:2 3 5 6 11
按照排序后这个顺序进行dp,当dp到11的时候,是dp[2][j]的值改变,序列中3,5,6的dp[i][j]中的i大于2,所以不会多求或者少求
而对于dp到6这个数,因为3,5,2这三个数在原序列中的位置就比它靠前,所以轮到求dp[5][j]的时候,2,3,5的dp值都已经求出来了
那么这个时候求出来的前缀和dp[1--4][j]就是正确的
这个自己可以模拟看下
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #include<map> #include<vector> #include<cstring> using namespace std; const int mod=1e9+7; const int maxn=1e3+5; #define mem(a) memset(a,0,sizeof(a)) //求sum(dp[1-x][j]) int n,m,dp[maxn][maxn]; struct shudui { int id,val; }que[maxn]; bool cmp(shudui x,shudui y) { if(x.val!=y.val) return x.val<y.val; return x.id>y.id; //如果两个val相等,因为题目要求严格递增,所以这样排序就可以满足题意 } int lowbit(int x) { return x&(-x); } void update(int x,int y,int val) //更新包含dp[x][y]的 { //后缀数组项 while(x<=n) { dp[x][y]=(dp[x][y]+val)%mod; x+=lowbit(x); } } int get_sum(int x,int y) { int sum=0; while(x>0) { sum=(sum+dp[x][y])%mod; x-=lowbit(x); } return sum; } int main() { int t,p=0; scanf("%d",&t); while(t--) { mem(dp); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&que[i].val); que[i].id=i; } sort(que+1,que+1+n,cmp); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(j==1) update(que[i].id,j,1); else //因为我们按照val排过序了,所以我们可以加上前缀和就行 { int sum=get_sum(que[i].id-1,j-1); update(que[i].id,j,sum); } } } printf("Case #%d: %d\n",++p,get_sum(n,m)); } return 0; }