您的位置:首页 > 理论基础 > 计算机网络

[网络流24题]最长递增子序列问题

2017-10-11 08:30 411 查看
最长递增子序列问题
题目描述:

给定正整数序列x1,...,xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。
设计有效算法完成(1)(2)(3)提出的计算任务。

输入格式:
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。

输出格式:
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。

输入样例#1:
4
3 6 2 5

输出样例#1:
2
2
3

题解:
(1).用dp解决就可以了,f[i]为以i为终点最长子序列的长度,再简单不过了。
(2).网络流解决吧,首先根据题意“取出”可知每个数只能选一次那么就直接想到拆点了(后面讲不拆点的方法)连边分四种情况:①若f[i]=1将i和s连一条容量为1的边。②若f[i]=s,将i+n和t连一条容量为1的边。③若f[i]=f[j]+1 && w[i]<w[j] 将j+n和i连一条容量为1的边。④将i和i+n连边
(3).第三问还比较好做,既然不限制使用次数,将容量设成inf就可以了。
还有拆点是必须拆的,因为出现重复选择的情况(但是没拆点的代码竟然过了?可能数据太水了吧)。例如:
5
8 1 9 11 10
建图如图




代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

const int max_m = 3000001;
const int max_n = 3001;

int f[max_n],w[max_n];
int point[max_n],nxt[max_m],v[max_m],remain[max_m];
int deep[max_n],cur[max_n];
int n,x,y,tot,s,t,inf,maxn,ans;

inline void clear()
{
memset(point,-1,sizeof(point));
memset(nxt,-1,sizeof(nxt));
for(int i=1; i<=n; ++i) f[i]=1;
tot=-1; inf=1e9; s=0; maxn=1; t=2*n+1; f
=1;
}

inline void addedge(int x,int y,int cal)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cal;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}

inline int dfs(int now,int t,int limit)
{
if(now==t || !limit) return limit;
int flow=0,f;

for(int i=cur[now]; i!=-1; i=nxt[i])
if(deep[v[i]]==deep[now]+1 && (f=dfs(v[i],t,min(remain[i],limit))))
{
flow+=f;
limit-=f;
remain[i]-=f;
remain[i^1]+=f;
if(!limit) break;
}

return flow;
}

inline bool bfs(int s,int t)
{
memset(deep,0x7f,sizeof(deep));
for(int i=s; i<=t; ++i)
cur[i]=point[i];

queue<int> q;
deep[s]=0; q.push(s);

while(!q.empty())
{
int now=q.front(); q.pop();
for(int i=point[now]; i!=-1; i=nxt[i])
if(deep[v[i]]>inf && remain[i])
{
deep[v[i]]=deep[now]+1;
q.push(v[i]);
}
}

return deep[t]<inf;
}

inline int dinic(int s,int t)
{
int ans=0;

while(bfs(s,t))
ans+=dfs(s,t,inf);

return ans;
}

int main()
{

scanf("%d",&n);
clear();

for(int i=1; i<=n; ++i)
scanf("%d",&w[i]);

for(int i=1; i<=n; ++i)//dp求最长递增子序列
for(int j=1; j<i; ++j)
if(w[j]<=w[i])
{
f[i]=max(f[j]+1,f[i]);
maxn=max(maxn,f[i]);
}
printf("%d\n",maxn);

for(int i=1; i<=n; ++i)
addedge(i,i+n,1);

for(int i=1; i<=n; ++i)
{
if(f[i]==1) addedge(s,i,1);
if(f[i]==maxn) addedge(i+n,t,1);
}

for(int i=1; i<=n; ++i)
for(int j=1; j<i; ++j)
if(w[j]<=w[i] && f[i]==f[j]+1) addedge(j+n,i,1);
printf("%d\n",ans=dinic(s,t));

addedge(s,1,inf);//将1和n容量设为inf
addedge(1,1+n,inf);
if(f
==maxn)
{
addedge(2*n,t,inf);
addedge(n,2*n,inf);
}

printf("%d\n",dinic(s,t)+ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: