[網絡流24題]最長遞增子序列問題

最長遞增子序列問題
題目描述:

給定正整數序列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[n]=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[n]==maxn)
	{
		addedge(2*n,t,inf);
		addedge(n,2*n,inf);
	}
	
	
	printf("%d\n",dinic(s,t)+ans);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章