洛谷 P2766 最长不下降子序列问题(网络流24题)

题目:最长不下降子序列问题


思路:

第一问, O ( n 2 ) O(n^2) O(n2)求LIS即可。
处理出 f [ i ] f[i] f[i]表示以i开头的不下降子序列个数,得出答案记为k。

第二问,先拆点,把一个点 x x x拆成 ( x , 0 ) (x,0) (x,0) ( x , 1 ) (x,1) (x,1),并把两者连边。
设置一个原点S和汇点T。
把S和所有 f [ i ] = = 1 f[i]==1 f[i]==1的点连边,把所有 f [ i ] = = k f[i]==k f[i]==k的点和T连边。
对于 f [ j ] = f [ i ] + 1 , j &lt; i f[j]=f[i]+1,j&lt;i f[j]=f[i]+1j<i且a[j]<=a[i],将 ( j , 1 ) (j,1) (j,1) ( i , 0 ) (i,0) (i,0)连边。
以上边权均为1,连边定义为从前者向后者引一条有向边。

第三问,只需在第二问的基础上,如果存在,将 [ S , ( 1 , 0 ) ] [S,(1,0)] [S,(1,0)] [ ( 1 , 0 ) , ( 1 , 1 ) ] [(1,0),(1,1)] [(1,0),(1,1)] [ ( n , 0 ) , ( n , 1 ) ] [(n,0),(n,1)] [(n,0),(n,1)] [ ( n , 1 ) , T ] [(n,1),T] [(n,1),T]这几条边权改为inf。


代码:

#include<bits/stdc++.h>
using namespace std;

struct Edge{
	int u,v,w;
	Edge(){}
	Edge(int uu,int vv,int ww){u=uu,v=vv,w=ww;}
};

#define maxn 500000
#define read(x) scanf("%d",&x)
#define inf ((int)1e9)

int n;
int a[maxn+5];

int f[maxn+5];

Edge e[maxn+5];
int h[maxn+5],nxt[maxn+5],cnt=-1;

int d[maxn+5];
int cur[maxn+5];

void readin() {
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
}

int LIS() {
	int s=0;
	for(int i=n;i>=1;i--) {
		for(int j=i;j<=n;j++) {
			if(a[j]>=a[i]) f[i]=max(f[i],1+f[j]);
		}
		s=max(s,f[i]);
	}
	return s;
}

void add(int u,int v,int w) {
	e[++cnt]=Edge(u,v,w);
	nxt[cnt]=h[u];
	h[u]=cnt;
}

void makeg(int k){
	memset(nxt,-1,sizeof(nxt)),memset(h,-1,sizeof(h)),cnt=-1;
	for(int i=1;i<=n;i++) if(f[i]==k) add(0,i,1),add(i,0,0);
	for(int i=1;i<=n;i++) if(f[i]==1) add(n+i,2*n+1,1),add(2*n+1,n+i,0);
	for(int i=1;i<=n;i++) add(i,n+i,1),add(n+i,i,0);
	for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]-f[i]==1) add(j+n,i,1),add(i,j+n,0);
}

void makeg2(int k) {
	memset(nxt,-1,sizeof(nxt)),memset(h,-1,sizeof(h)),cnt=-1;
	for(int i=2;i<=n;i++) if(f[i]==k) add(0,i,1),add(i,0,0);
	if(f[1]==k) add(0,1,inf),add(1,0,0);
	for(int i=1;i<n;i++) if(f[i]==1) add(n+i,2*n+1,1),add(2*n+1,n+i,0);
	if(f[n]==1) add(2*n,2*n+1,inf),add(2*n+1,2*n,0);
	for(int i=2;i<n;i++) add(i,n+i,1),add(n+i,i,0);
	add(1,1+n,inf),add(1+n,1,0);
	add(n,2*n,inf),add(2*n,n,0);
	for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[j]<=a[i]&&f[j]-f[i]==1) add(j+n,i,1),add(i,j+n,0);
}

queue<int> que;

bool bfs() {
	memset(d,0,sizeof(d));
	d[0]=1;
	que.push(0);
	while(!que.empty()) {
		int x=que.front();que.pop();
		for(int i=h[x];~i;i=nxt[i]) {
			Edge y=e[i];
			if(y.w==0||d[y.v]) continue;
			d[y.v]=d[x]+1;
			que.push(y.v);
		}	
	}
	if(d[2*n+1]>0) return true;
	return false;
}

int dfs(int x,int w) {
	if(x==2*n+1) return w;
	for(int& i=cur[x]; ~i;i=nxt[i]) {
		Edge y=e[i];
		if(d[y.v]-d[x]!=1||!y.w) continue;
		int z=dfs(y.v,min(w,y.w));
		if(z) {
			e[i].w-=z;
			e[i^1].w+=z;
			return z;
		}
	}
	return 0;
}

int dinic() {
	int ans=0;
	while(bfs()) {
		for(int i=0;i<=2*n+1;i++) cur[i]=h[i];
		while(int x=dfs(0,inf)) ans+=x;
	}
	return ans;
}

int main() {
	readin();
	int k=LIS();
	printf("%d\n",k);
	
	makeg(k);
	int ans1=dinic();
	
	makeg2(k);
	int ans2=dinic();
	
	printf("%d\n%d",ans1,ans2);
	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值