先说说最长上升子序列的模板,时间复杂度O(nlogn)
最长上升子序列 #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 41000; int a[N],p; //a[i] 原始数据 int d[N]; //d[i] 长度为i的递增子序列的最小值 int BinSearch(int key, int low, int high)//二分查找,找不到则归0 { while(low<=high) { int mid = (low+high)>>1; if(key>d[mid] && key<=d[mid+1]) return mid; else if(key>d[mid]) low = mid+1; else high = mid-1; } return 0; } int LIS() { int i,j; d[1] = a[1]; int len = 1; //递增子序列长度 for(i = 2; i <= p; i++) { if(d[len]<a[i]) j = ++len; else j = BinSearch(a[i],1,len) + 1; d[j] = a[i]; } return len; } int main() { // freopen("in.txt","r",stdin); int t; scanf("%d",&t); while(t--) { scanf("%d",&p); for(int i = 1; i <= p; i++) scanf("%d",&a[i]);//输入的数组 printf("%d ",LIS()); } return 0; }
dp[i]表示的是到目前为止,当子序列的长度为i时,对应数组a[]中的最小值,用len记录到目前为止可以组成的子序列的最长长度
当处理一个新的a[u]时,a[u]>dp[len]则直接dp[len+1]=a[u];
当a[u]<=dp[len]容易看出dp是一个严格单调递增的数组,则直接二分求解a[u]可以对dp的哪个元素进行更新
这题的题意是:
给你一堆乱序序列包含0,0可以变成任何整数包括负数,问最长上升子序列的长度
自己乱搞是在LIS模板的基础上碰到0就去更新dp数组每一个元素的值,若0在队首就将dp[1]=-1e9+7;
没想到居然过了大概数据比较水
#include <iostream> #include <map> #include <math.h> #include <algorithm> #include <vector> #include <cstdlib> #include <cstdio> #include <cstring> #include <set> #include<stdio.h> using namespace std; const int N=1e5+50; const int M=1e9+7; int t,n,a[N],dp[N]; int erfen(int l,int r,int u){ while(l<=r){ int mid=(l+r)>>1; if(dp[mid]<a[u]&&dp[mid+1]>=a[u]) return mid; else if(dp[mid]>a[u]) r=mid-1; else l=mid+1; } return 0; } int main() { // freopen("in.txt","r",stdin); cin>>t; int cas=0; while(t--){ cas++; scanf("%d",&n); for(int i=1;i<=n;i++) dp[i]=1000000+10; dp[0]=0; int i,j; for(i = 1;i <= n;i ++) scanf("%d",&a[i]); int len=0; dp[1]=min(dp[1],a[1]); len++; // cout<<dp[1]<<" "<<a[1]<<endl; for(i = 2;i <= n;i ++){ //cout<<"r"<<endl; if(a[i]!=0){ if(a[i]>dp[len]) j=++len; else j=erfen(1,len,i)+1; dp[j]=a[i]; } else{ dp[len+1]=dp[len]+1; for(j=len;j>1;j--) dp[j]=dp[j-1]+1; dp[1]=0; len++; } } printf("Case #%d: %d ",cas,len); } return 0; }
题解是:
0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的,因为0放不进去的原因无非就是非严格递增。为了保证严格递增,我们可以将每个权值S[i]减去i前面0的个数,再做LIS,就能保证结果是严格递增的。因此我们可以把0拿出来,对剩下的做O(nlogn)的LIS,统计结果的时候再算上0的数量。只能说是膜拜。当然有点小细节,自己注意一下
#include <iostream> #include <map> #include <math.h> #include <algorithm> #include <vector> #include <cstdlib> #include <cstdio> #include <cstring> #include <set> #include<stdio.h> using namespace std; const int N=1e5+50; const int M=1e9+7; int t,n,a[N],dp[N],num[N]; int erfen(int l,int r,int u){ while(l<=r){ int mid=(l+r)>>1; if(dp[mid]<a[u]&&dp[mid+1]>=a[u]) return mid; else if(dp[mid]<a[u]) l=mid+1; else r=mid-1; } return 0; } int main() { // freopen("in.txt","r",stdin); cin>>t; int cas=0; while(t--){ int cnt0=0; cas++; scanf("%d",&n); for(int i=1;i<=n;i++) dp[i]=1000000+10,num[i]=0; dp[0]=0,num[0]=0; int i,j,cnt=0; for(i = 1;i <= n;i ++){ scanf("%d",&a[i]); if(a[i]==0) cnt++,cnt0++; if(a[i]!=0) num[i]=cnt; } for(i=1,cnt=0;i<=n;i++){ if(a[i]!=0){ a[++cnt]=a[i]-num[i]; //cout<<a[cnt]<<" "<<cnt<<endl; } //cout<<a[cnt]<<" "<<cnt<<endl; } int len=0; dp[1]=min(dp[1],a[1]); len++; if(cnt==0) len=0; // cout<<dp[1]<<" "<<a[1]<<endl; for(i = 2;i <= cnt;i ++){ if(a[i]>dp[len]) j=++len; else j=erfen(1,len,i)+1; dp[j]=a[i]; //cout<<j<<" "<<dp[j]<<endl; } printf("Case #%d: %d ",cas,len+cnt0); } return 0; }