最长不下降子序列问题
Description
给定正整数序列
x1,…,xn
。
(1)计算其最长不下降子序列的长度
s
。
(2)计算从给定的序列中最多可取出多少个长度为 s 的不下降子序列。
(3)如果允许在取出的序列中多次使用
x1
和
xn
,则从给定序列中最多可取出多少个长
度为
s
的不下降子序列。
文件第 1 行有
1
个正整数 n,表示给定序列的长度。
接下来的
1
行有 n 个正整数
x1,…,xn
。
Output
程序运行结束时,将任务(1)(2)(3)的解答输出。
第
1
行是最长不下降子序列的长度 s。
第
2
行是可取出的长度为 s 的不下降子序列个数。
第
3
行是允许在取出的序列中多次使用 x1 和
xn
时可取出的长度为
s
的不下降子序列个数。
4
3 6 2 5
Sample Output
2
2
3
Solution
第一问用动态规划。因为数据规模不大,O(n2) 可以过。
第二问用网络流。
若存在
j>i
,并且
fj=fi+1
(
fi
表示以
i
为结束的最长不下降子序列的长度),那么将 i 与
j
连一条边。
连接 i 与
s
,当 fi=1。
连接
i
与 t,当
fi=ans1
。
求最大流即可。
第三问,将第一个点和最后一个点连的边的权值设为
∞
<script type="math/tex" id="MathJax-Element-1385">∞</script> 即可。
Code
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <queue>
-
- #define Max(x,y) ((x)>(y)?(x):(y))
- #define Min(x,y) ((x)<(y)?(x):(y))
- #define ss 0
- #define tt 900
- #define INF 0x3f3f3f3f
-
- using namespace std;
-
- int n,ans1,cnt;
- int s[1000];
- int f[1000];
- int head[1000];
- int nxt[1000010];
- int data[1000010];
- int flow[1000010];
- int dis[1000];
- queue<int>q;
-
- void add(int x,int y,int z){
- nxt[cnt]=head[x];data[cnt]=y;flow[cnt]=z;head[x]=cnt++;
- nxt[cnt]=head[y];data[cnt]=x;flow[cnt]=0;head[y]=cnt++;
- }
-
- bool BFS(){
- memset(dis,-1,sizeof dis);
- dis[ss]=0;
- q.push(ss);
- while(!q.empty()){
- int now=q.front();
- q.pop();
- for(int i=head[now];i!=-1;i=nxt[i]){
- if(flow[i]&&dis[data[i]]==-1){
- q.push(data[i]);
- dis[data[i]]=dis[now]+1;
- }
- }
- }
- return dis[tt]>0;
- }
-
- int dfs(int now,int low){
- if(now==tt)return low;
- int Low;
- for(int i=head[now];i!=-1;i=nxt[i]){
- if(flow[i]&&dis[data[i]]==dis[now]+1){
- if(Low=dfs(data[i],Min(low,flow[i]))){
- flow[i]-=Low;
- flow[i^1]+=Low;
- return Low;
- }
- }
- }
- return 0;
- }
-
- int main(){
- freopen(”alis.in”,“r”,stdin);
- freopen(”alis.out”,“w”,stdout);
- scanf(”%d”,&n);
- memset(head,-1,sizeof head);
- for(int i=1;i<=n;i++)scanf(“%d”,&s[i]);
- f[1]=1;
- for(int i=2;i<=n;i++){
- f[i]=1;
- for(int j=1;j<i;j++)
- if(s[i]>=s[j])f[i]=Max(f[i],f[j]+1);
- ans1=Max(ans1,f[i]);
- }
- printf(”%d\n”,ans1);
- for(int i=1;i<=n;i++){
- if(f[i]==1)add(ss,i,1);
- if(f[i]==ans1)add(i,tt,1);
- for(int j=i+1;j<=n;j++)
- if(f[j]==f[i]+1&&s[j]>=s[i])add(i,j,1);
- }
- int flag,ans2=0;
- while(BFS())
- while(flag=dfs(ss,INF))ans2+=flag;
- printf(”%d\n”,ans2);
- if(ans1==1){
- printf(”%d\n”,ans2);
- return 0;
- }
- cnt=0;
- memset(head,-1,sizeof head);
- memset(data,0,sizeof data);
- memset(nxt,0,sizeof nxt);
- memset(flow,0,sizeof flow);
- for(int i=1;i<=n;i++){
- if(f[i]==1){
- if(i!=1&&i!=n)
- add(ss,i,1);
- else add(ss,i,INF);
- }
- if(f[i]==ans1){
- if(i!=1&&i!=n)
- add(i,tt,1);
- else add(i,tt,INF);
- }
- for(int j=i+1;j<=n;j++)
- if(f[j]==f[i]+1&&s[j]>=s[i]){
- if(ans1==2&&i==1&&j==n){
- add(i,j,1);
- continue;
- }
- if(i==1||i==n||j==1||j==n)
- add(i,j,INF);
- else add(i,j,1);
- }
- }
- int ans3=0;
- while(BFS())
- while(flag=dfs(ss,INF))ans3+=flag;
- printf(”%d\n”,ans3);
- return 0;
- }