poj1952 BUY LOW, BUY LOWER[线性DP(统计不重复LIS方案)]

如题。$N \leqslant 5000$。


感觉自己思路永远都是弯弯绕绕的。。即使会做也会被做繁掉。。果然还是我太菜了。


递减不爽,先倒序输入算了。第一问做个LIS没什么说的。第二问统计个数,考虑什么时候是重复计算的。$g[i]$表示第$i$个数结尾的LIS长度的方案(不重复)。当统计时dp到一个数时显然从前面满足$f_j+1=f_i且A_j<A_i$条件的累加过来,$A_j$不同的时候,自然不会有重复;当有相同的数且f一样时,如果这几种都加入,就重复了。而相同的几个数字显然靠后的方案统计到的更多,所以每次只取最靠右的那个数累加上去即可。实现上,开一个桶,记录vis,结束后再吐出来。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 8 #define _dbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
 9 using namespace std;
10 typedef long long ll;
11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
15 template<typename T>inline T read(T&x){
16     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
17     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
18 }
19 const int N=5000+7,M=1<<17,INF=0x3f3f3f3f;
20 int f[N],g[N],lis[N],a[N],vis[M],bin[N];
21 int n,len,ans,tot,cnt;
22 
23 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
24     read(n);for(register int i=1;i<=n;++i)read(a[n-i+1]);
25     lis[len=1]=INF;
26     for(register int i=1;i<=n;++i){
27         f[i]=lower_bound(lis+1,lis+len+1,a[i])-lis;
28         if(f[i]>len)lis[++len]=a[i];else lis[f[i]]=a[i];
29         MAX(ans,f[i]);
30     }
31     for(register int i=1;i<=n;++i){
32         if(f[i]==1){g[i]=1;continue;}
33         for(register int j=i-1;j;--j)if(!vis[a[j]]&&a[j]<a[i]&&f[j]+1==f[i])vis[bin[++tot]=a[j]]=1,g[i]+=g[j];
34         while(tot)vis[bin[tot--]]=0;
35     }
36     for(register int i=n;i;--i) if(!vis[a[i]]&&f[i]==ans)cnt+=g[i],vis[a[i]]=1;
37     printf("%d %d\n",ans,cnt);
38     return 0;
39 }
View Code

嗯这个是本人极其繁琐的想法。发现自己傻了有没有。。而且数据大的话桶不就挂了吗。。所以依据原来思路,改变对于dp状态的定义。$g[i]$表示第$i$个数结尾的长度为LIS的方案数,且不包括之前所有和自己相同且$len_{LIS}$相同的数的方案。这样每次转移时遇到相同即break。保证取的决策一定来源于上一个相同数和现在这个数之间。具体还是看code吧。。

另外还有一种做法是网络流。。不想写了QwQ。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 8 #define _dbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
 9 using namespace std;
10 typedef long long ll;
11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
15 template<typename T>inline T read(T&x){
16     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
17     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
18 }
19 const int N=5000+7,M=1<<17,INF=0x3f3f3f3f;
20 int f[N],g[N],lis[N],a[N];
21 int n,len,ans,tot,cnt;
22 
23 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
24     read(n);for(register int i=1;i<=n;++i)read(a[n-i+1]);
25     lis[len=1]=INF;
26     for(register int i=1;i<=n;++i){
27         f[i]=lower_bound(lis+1,lis+len+1,a[i])-lis;
28         if(f[i]>len)lis[++len]=a[i];else lis[f[i]]=a[i];
29         MAX(ans,f[i]);
30     }
31     g[0]=1;
32     for(register int i=1;i<=n;++i){
33         for(register int j=i-1;~j;--j){
34             if(a[i]==a[j]&&f[i]==f[j])break;
35             if(f[i]==f[j]+1&&a[j]<a[i])g[i]+=g[j];
36         }
37     }
38     for(register int i=1;i<=n;++i)if(f[i]==ans)cnt+=g[i];
39     printf("%d %d\n",ans,cnt);
40     return 0;
41 }

猜你喜欢

转载自www.cnblogs.com/saigyouji-yuyuko/p/10720428.html
今日推荐