• 最长不下降子序列问题


    题目建模很好

    建模思路转自:byvoid(dalao十年前的题解Orz)

    「问题分析」
    第一问是LIS,动态规划求解,第二问和第三问用网络最大流解决。

    「建模方法」
    首先动态规划求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K。

    1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。
    2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。
    3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。
    4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。

    求网络最大流,就是第二问的结果。把边(<1.a>,<1.b>)(<N.a>,<N.b>)(S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。

    「建模分析」
    上述建模方法是应用了一种分层图的思想,把图每个顶点i按照F[i]的不同分为了若干层,这样图中从S出发到T的任何一条路径都是一个满足条件的最长上升子序列。由于序列中每个点要不可重复地取出,需要把每个点拆分成两个点。单位网络的最大流就是增广路的条数,所以最大流量就是第二问结果。第三问特殊地要求x1和xn可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using std::memset;
    using std::max;
    using std::queue;
    using std::memcpy;
    const int inf=0x3f3f3f3f;
    const int N=3005,S=0,T=1001;
    int n,a[N],f[N],ecnt=1,head[N],cur[N];
    struct Edge {
    	int to,nxt,val;
    } e[N<<2];
    void add(int bg,int ed,int val) {
    	e[++ecnt].nxt=head[bg];
    	e[ecnt].to=ed;
    	e[ecnt].val=val;
    	head[bg]=ecnt;
    }
    void insert(int a,int b,int v) {
    	add(a,b,v);
    	add(b,a,0);
    }
    int h[N];
    queue<int>q;
    bool bfs() {
    	memset(h,-1,sizeof h);
    	q.push(S);
    	h[S]=0;
    	while(!q.empty()) {
    		int x=q.front();
    		q.pop();
    		for(int i=head[x]; i; i=e[i].nxt) {
    			if(e[i].val&&h[e[i].to]==-1) {
    				h[e[i].to]=h[x]+1;
    				q.push(e[i].to);
    			}
    		}
    	}
    	return h[T]!=-1;
    }
    int dfs(int x,int f) {
    	if(x==T)return f;
    	int tp,used=0;
    	for(int i=cur[x]; i; i=e[i].nxt) {
    		if(e[i].val&&h[e[i].to]==h[x]+1) {
    			tp=dfs(e[i].to,std::min(e[i].val,f-used));
    			e[i].val-=tp;
    			if(e[i].val)cur[x]=i;
    			e[i^1].val+=tp;
    			used+=tp;
    			if(used==f)return f;
    		}
    	}
    	if(!used)h[x]=-1;
    	return used;
    }
    int maxflow;
    void dinic() {
    	maxflow=0;
    	while(bfs()) {
    		memcpy(cur,head,sizeof head);
    		maxflow+=dfs(S,inf);
    	}
    }
    int main() {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    	for(int i=1; i<=n; i++) {
    		f[i]=1;
    		for(int j=1; j<i; j++)
    			if(a[i]>=a[j]) {
    				insert(j+500,i,1);
    				f[i]=max(f[i],f[j]+1);
    				f[n+1]=max(f[n+1],f[i]);
    			}
    	}
    	f[n+1]=max(f[n+1],1);
    	printf("%d
    ",f[n+1]);
    	if(f[n+1]==1){
    		printf("%d
    %d",n,n);return 0;
    	}
    	for(int i=1; i<=n; i++) {
    		insert(i,i+500,1);
    		if(f[i]==f[n+1]) insert(i+500,T,1);
    		if(f[i]==1) insert(0,i,1);
    	}
    	for(int i=1;i<=n;i++) 
    		for(int j=i+1;j<=n;j++) {
    			if(a[j]>=a[i]&&f[j]==f[i]+1) insert(i+500,j,1);
    		}
    	dinic();
    	printf("%d
    ",maxflow);
    	memset(head,0,sizeof head);ecnt=1;
    	for(int i=1,v;i<=n;i++) {
    		v=1;
    		if(i==1||i==n) v=inf;
    		insert(i,i+500,v);
    		if(f[i]==f[n+1]) insert(i+500,T,v);
    		if(f[i]==1) insert(0,i,v);
    	}
    	for(int i=1;i<=n;i++) 
    		for(int j=i+1;j<=n;j++) 
    			if(a[j]>=a[i]&&f[j]==f[i]+1) insert(i+500,j,1);
    	dinic();
    	printf("%d",maxflow);
    
    }
    
    我是咸鱼。转载博客请征得博主同意Orz
  • 相关阅读:
    RecyclerView 数据刷新的几种方式 局部刷新 notify MD
    【图片】批量获取几万张图片
    RV BaseRecyclerViewAdapterHelper 总结 MD
    RecyclerView.ItemDecoration 间隔线
    Kotlin【简介】Android开发 配置 扩展
    Kotlin 特性 语法糖 优势 扩展 高阶 MD
    一个十分简洁实用的MD风格的UI主框架
    折叠伸缩工具栏 CollapsingToolbarLayout
    FloatingActionButton FAB 悬浮按钮
    Glide Picasso Fresco UIL 图片框架 缓存 MD
  • 原文地址:https://www.cnblogs.com/sdfzhsz/p/9265006.html
Copyright © 2020-2023  润新知