博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HDU 3998 Sequence (最长上升子序列+最大流)
阅读量:4649 次
发布时间:2019-06-09

本文共 2670 字,大约阅读时间需要 8 分钟。

参考链接:

题意:求一个序列的最长上升子序列,及其个数(注意:两个最长上升子序列不能共有同一个数,才能算两个)

思路:用dp+最大流来求,首先用两次循环dp求出最长上升子序列的长度ans,然后就是建图了,可以选择源点为0,

由于数列中的每一个数只能使用一次,构图的时候需要拆点。若有n个数,则拆成2 * n个点,构造源点s=0和汇点t=2*n+1,
将每个点(i)都和自己拆出来的点(i+n)连边,将源点和dp[i]=1的点连边,将dp[i]=ans的点与汇点连边,
最后若dp[j] = dp[i] + 1,则将i + n和 j连边。所有边的流量都是1,这样便可以限制每个点只使用一次。
其实连边的顺序就是最长序列为1,2,3,...,ans。可以理解为从最长序列为1(该数本身)一直流到数列中的最长上升序列。

这里要注意的是,由于数据弱,有不用拆点也能A的。但是为了保证每个点都是用一次,一定要拆点。

如果不拆点的话,可以见下图,则中间那个点可能会被用两次,具体不仔细说了。

 

我一开始按照dinic模板打了一遍,结果TLE。。。后来经别人指点,加了dinic中的两个判断条件,见(1)和(2),瞬间A了。

 

#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3fusing namespace std;const int maxn=1005;int n,m;int a[maxn]; //序列int pri[maxn*2];int head[maxn*2]; //因为要拆点int s,t; //s:源点 t:汇点int dp[maxn];int tot=0;int ans,num;//最长上升子序列的长度,以及不同的个数。struct Edge{ int from,to; int next; int c,f;}edge[maxn*100];void add(int x,int y,int val){ edge[tot].next=head[x]; edge[tot].from=x; edge[tot].to=y; edge[tot].c=val; //注意这里是c为val,反向边c为0 //edge[tot].f=0; head[x]=tot++; edge[tot].next=head[y]; edge[tot].from=y; edge[tot].to=x; edge[tot].c=0; //edge[tot].f=0; head[y]=tot++;}bool BFS(){ queue
q; memset(pri,-1,sizeof(pri)); pri[0]=0;//源点 q.push(0); while(!q.empty()){ int tmp=q.front(); q.pop(); for(int k=head[tmp];k!=-1;k=edge[k].next){ int v=edge[k].to; if(pri[v]==-1 && edge[k].c>0){ pri[v]=pri[tmp]+1; if(v==t){ return true; } q.push(v); } } } return false;}int dinic(int p,int flow){ if(p==t){ return flow; } int f=flow; for(int k=head[p];k!=-1;k=edge[k].next){ int v=edge[k].to; if(pri[v]==pri[p]+1 && edge[k].c>0){ int a=edge[k].c; //a为该边可以增加的流量 int ff=dinic(v,min(a,flow)); //ff为路径中所有a的最小值,即为该条路中可以增加的流量 //edge[k].f+=ff; //edge[k^1].f-=ff; //因为双向边是同时建的,编号必定一奇一偶成对的,如0、1;2、3;4、5; //这样用k异或1即可求得对应的反向边 edge[k].c-=ff; edge[k^1].c+=ff; flow-=ff; if(flow<=0) break; //(1) } } if(f-flow<=0) pri[p]=-1; //(2) //如果从p点流出去的流量<=0,那么设置pri[p]的值为-1,之后在dinic中就不考虑到p点的情况了。 return f-flow;}int main(){ while(scanf("%d",&n)!=EOF){ for(int i=1;i<=n;i++){ dp[i]=1; scanf("%d",&a[i]); } for(int i=2;i<=n;i++){ for(int j=1;j
a[j] && dp[j]+1>dp[i]) dp[i]=dp[j]+1; } } m=2*n+1; ans=0; for(int i=1;i<=n;i++){ ans=max(dp[i],ans); } memset(head,-1,sizeof(head)); tot=0; s=0; t=2*n+1; 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=1;i<=n;i++){ //下面j只要从i+1开始即可,因为add加边是加双向边的 for(int j=i+1;j<=n;j++){ if(dp[j]==dp[i]+1){ add(i+n,j,1); } } } num=0; while(BFS()){ num+=dinic(s,INF); } printf("%d\n%d\n",ans,num); } return 0;}
View Code

 

转载于:https://www.cnblogs.com/chenxiwenruo/p/3617412.html

你可能感兴趣的文章
转过来的,可以看下
查看>>
windows搭建SVN服务MD版
查看>>
HashMap的工作原理
查看>>
一碗饭
查看>>
floyd求最小环 模板
查看>>
SqlServer索引的原理与应用
查看>>
使用Kubeadm搭建Kubernetes(1.12.2)集群
查看>>
微信小程序获取当前地址以及选择地址详解 地点标记
查看>>
任务平均分配的小算法
查看>>
学习日报 7-10(验证码)
查看>>
No.3 - CSS transition 和 CSS transform 配合制作动画
查看>>
c++STL全排列
查看>>
开发系列:03、Spark Streaming Custom Receivers(译)
查看>>
fixed与sticky的区别
查看>>
keil C51 例子
查看>>
MVC后台数据赋值给前端JS对象
查看>>
win7、offcie 2010是否激活查看方法
查看>>
Linux下使用wget下载FTP服务器文件
查看>>
Java基础 【Arrays 类的使用】
查看>>
MPI 环境搭建问题-运行程序闪退
查看>>