题目
求序列的最长不下降子序列的长度及最长不下降序列的个数
还有求若第一个数和最后一个数可以取无限次,那么会有多少个最长不下降序列
分析
第一问动态规划
F
i
=
max
{
F
j
+
1
}
F_i=\max\{F_j+1\}
Fi=max{Fj+1}
第二问按照方向建边拆点跑最大流,第三问修改无限大。
代码
#include <cstdio>
#include <cctype>
#include <queue>
#define max(a,b) (a>b?a:b)
using namespace std;
struct node{int y,w,next;}e[50001];
int n,a[501],dp[501],s,t,dis[1003],ans1,ans,k=1,ls[1003];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void add(int x,int y,int w){e[++k]=(node){y,w,ls[x]}; ls[x]=k; e[++k]=(node){x,0,ls[y]}; ls[y]=k;}
bool bfs(int s){
for (int i=1;i<=t;i++) dis[i]=0;
queue<int>q; q.push(s); dis[s]=1;
while (q.size()){
int x=q.front(); q.pop();
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&!dis[e[i].y]){
dis[e[i].y]=dis[x]+1;
if (e[i].y==t) return 1;
q.push(e[i].y);
}
}
return 0;
}
int dinic(int x,int now){
if (x==t||!now) return now;
int rest=0,f;
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
rest+=(f=dinic(e[i].y,min(now-rest,e[i].w)));
e[i].w-=f; e[i^1].w+=f;
if (now==rest) return rest;
}
if (!rest) dis[x]=0;
return rest;
}
void update(int x,int ny,int y){
for (int i=ls[x];i;i=e[i].next){
if (e[i].y==ny) e[i^1].w=1e4;
if (e[i].y==y) e[i].w=1e4;
}
}
int main(){
n=in(); s=n<<1|1,t=s+1;
for (int i=1;i<=n;i++) a[i]=in(),dp[i]=1;
for (int i=2;i<=n;i++)
for (int j=1;j<i;j++)
if (a[j]<=a[i]) dp[i]=max(dp[i],dp[j]+1);//动态规划
for (int i=1;i<=n;i++) ans=max(ans,dp[i]); printf("%d\n",ans);//求最大值
for (int i=1;i<=n;i++){
add(i,i+n,1); //
if (dp[i]==1) add(s,i,1); //如果可开始不下降序列
if (dp[i]==ans) add(i+n,t,1);//如果找到终点
}
for (int i=2;i<=n;i++)
for (int j=1;j<i;j++)
if (a[j]<=a[i]&&dp[i]==dp[j]+1) add(j+n,i,1);//建边
while (bfs(s)) ans1+=dinic(s,1e6); printf("%d\n",ans1);//跑最大流
update(1,s,1+n); if (dp[n]==ans) update(n<<1,n,t);
while (bfs(s)) ans1+=dinic(s,1e6);
return !printf("%d",ans1);
}