#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;
}
[網絡流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
建圖如圖
:
代碼:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.