最长不下降子序列问题

link

比较基础的题目。

由于一个数只能被使用一次,考虑拆点,然后在可能转移的节点之间连边。由于所求的LIS长度一定,所以源点只能连 1 点,同时只有值为 LIS 的点才能连向汇点(其实这种情况两边卡死是很好做的,就怕它搞什么两边留空),这样跑出来的一定是特定长度的 LIS,跑最大流即可。对于第三问把首尾两个分点之间的限制去掉即可。但这道题有一些坑点,比如当len为1或2时需要特殊处理,不然由于首尾不受限制跑出来会是无限大。而len大于2时不降序列中肯定会有一个受限制的点,既然那个点受限制整个序列也会受限制,也就不会跑出个无穷大了。

由于我太弱了竟然在数组问题上死掉了。每日维生素B。

#include<bits/stdc++.h>
//#define feyn
const int N=510;
const int M=3e6;
const int maxn=1e8;
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

int m,c[N],f[N],ss,tt,a[N],b[N],cnt,ans;
struct edge{
	int t,v,next;
}e[M];
int head[N<<2],esum=1;
inline void adde(int fr,int to,int val){
	e[++esum]=(edge){to,val,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int val){
	//printf("add %d %d %d\n",fr,to,val);
	adde(fr,to,val);adde(to,fr,0);
}

int q[N<<2],ll,rr,t[N<<2],nt,d[N<<2];
inline bool check(){
	t[q[ll=rr=1]=ss]=++nt;d[ss]=1;
	while(ll<=rr){
		int wh=q[ll++];//printf("wh=%d\n",wh);
		for(int i=head[wh],th;i;i=e[i].next){
			if(t[th=e[i].t]==nt||e[i].v==0)continue;
			d[th]=d[wh]+1,t[th]=nt,q[++rr]=th;
		}
	}
	return t[tt]==nt;
}
inline int dinic(int wh,int val){
	//printf("dinic %d %d\n",wh,val);
	if(wh==tt)return val;
	int used=0;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v==0||d[th=e[i].t]!=d[wh]+1)continue;
		int now=dinic(th,min(e[i].v,val));
		if(now)val-=now,used+=now,e[i].v-=now,e[i^1].v+=now;
		if(val==0)break;
	}
	if(val)d[wh]=-1;return used;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	#endif
	
	read(m);ss=++cnt,tt=++cnt;
	for(int i=1;i<=m;i++)read(c[i]),a[i]=++cnt,b[i]=++cnt,add(a[i],b[i],1);
	for(int i=1;i<=m;i++){
		for(int j=1;j<i;j++){
			if(c[j]<=c[i])f[i]=max(f[i],f[j]);
		}
		f[i]++;ans=max(ans,f[i]);
	}
	printf("%d\n",ans);
	
	for(int i=1;i<=m;i++){
		if(f[i]==1)add(ss,a[i],1);
		for(int j=1;j<i;j++){
			if(c[j]<=c[i]&&f[j]+1==f[i])add(b[j],a[i],1);
		}
		if(f[i]==ans)add(b[i],tt,1);
	}
	int an=0;
	while(check())an+=dinic(ss,maxn);
	printf("%d\n",an);an=0;
	
	if(ans==1){
		printf("%d\n",m);
		return 0;
	}
	memset(head,0,sizeof(head));esum=1;
	for(int i=1;i<=m;i++)add(a[i],b[i],i==1||i==m?maxn:1);
	for(int i=1;i<=m;i++){
		if(f[i]==1)add(ss,a[i],i==1||i==m?maxn:1);
		for(int j=1;j<i;j++){
			if(c[j]<=c[i]&&f[j]+1==f[i]&&(j==1&&i==m)==false)add(b[j],a[i],1);
		}
		if(f[i]==ans)add(b[i],tt,i==1||i==m?maxn:1);
	}
	while(check())an+=dinic(ss,maxn);
	if(ans==2&&c[1]<=c[m])an++;
	printf("%d\n",an);
	
	return 0;
}
posted @ 2022-07-14 17:30  Feyn618  阅读(24)  评论(0编辑  收藏  举报