給定正整數序列X1....... Xn 。
(1)計算其最長遞增子序列的長度s。
(2)計算從給定的序列中最多可取出多少個長度爲s的遞增子序列。
(3)如果允許在取出的序列中多次使用X1和Xn,則從給定序列中最多可取出多少個長
度爲s的遞增子序列。
編程任務:
設計有效算法完成(1)(2)(3)提出的計算任務。
數據輸入:
由文件input.txt提供輸入數據。文件第1 行有1個正整數n,表示給定序列的長度。接
下來的1 行有n個正整數X1......Xn 。
結果輸出:
程序運行結束時,將任務(1)(2)(3)的解答輸出到文件output.txt中。第1 行是最長
遞增子序列的長度s。第2行是可取出的長度爲s 的遞增子序列個數。第3行是允許在取出
的序列中多次使用x1和xn時可取出的長度爲s 的遞增子序列個數。
輸入示例:
4
3 6 2 5
輸出示例
2
2
3
分析:
首先要說的是示例數據第三個不應該是3,先放着,到後面在談。首先第一問是經典的LIS問題,即最長遞增子序列,用DP很容易就解出來了,f(i)表示以i結尾的序列的最長遞增子序列長度。狀態轉移方程是:f(i)=max{ f(j) | j<i,a[j]<a[i]}+1。這是一個O(n^2)的算法,有更快的O(nlogn),不過我沒寫,因爲數據規模很小,O(n^2)夠了。以後準備寫一個整理算法的專欄,到時候會介紹。
然後,建立最大流模型,這個模型就是有節點容量的網絡流模型,把每個點拆成入點和出點,連接出入點,邊權爲1表示每個點只能選擇一次。創建源點和匯點,連接源點和每個f[i]=1的點,連接匯點和每個f[i]=s的點,然後對於每個兩個點,如果f[i]+1=f[j],則連接i點和j點,邊權爲正無窮。然後求最大流就可。
第三問我感覺有點小漏洞,不知道是我想錯了還是什麼,就是把X1,Xn出入點邊權正無窮,然後把源點和X1入點正無窮,如果f[n]=s,就把Xn出點和匯點連接邊權正無窮,然後再求一次最大流。
可是這樣有一個bug,如果f[n]=2,就會產生錯誤,因爲從源點->X1->Xn->匯點邊權都是正無窮。所以感覺題目出的很怪異,不過暫且就這麼解吧,可能是我目前境界不夠。所以如果複製了這個代碼,運行出錯,千萬別怪我。
或者有哪位大牛能指出我的BUG,感激不盡。
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 660;
const int INF = 1<<30;
struct edge{
int from,to,cap,flow;
edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d)
{}
};
vector<int> g[maxn];
vector<edge> edges;
int a[maxn];
int f[maxn];
bool visit[maxn];
int d[maxn];
int cur[maxn];
int t,s;
void addedge(int from,int to,int cap)
{
edges.push_back(edge(from,to,cap,0));
edges.push_back(edge(to,from,0,0));
int m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
void buildgraph(int n,int m)
{
s=0;t=2*n+1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++)
if(f[i]+1==f[j]) addedge(i+n,j,INF);//連接的邊權爲無窮
addedge(i,i+n,1);//拆點爲2個,權值爲1,保證每個點只選一次
if(f[i]==1) addedge(s,i,1);
if(f[i]==m) addedge(i+n,t,1);
}
}
bool BFS()
{
memset(visit,false,sizeof(visit));
d[s]=0;
queue<int> q;
q.push(s);
visit[s]=true;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(!visit[e.to]&&e.cap>e.flow){
visit[e.to]=true;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return visit[t];
}
int DFS(int x,int a)
{
if(x==t||a==0) return a;
int flow=0,f;
for(int &i=cur[x];i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
flow+=f;
a-=f;
e.flow+=f;
edges[g[x][i]^1].flow-=f;
if(a==0) break;
}
}
return flow;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
//第一問,DP求LIS
int max=0;
f[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++)
if(a[i]>a[j]&&f[j]>f[i]) f[i]=f[j];
f[i]++;
if(f[i]>max) max=f[i];
}
printf("%d\n",max);
//第二問
buildgraph(n,max);
int maxflow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
maxflow+=DFS(s,INF);
}
printf("%d\n",maxflow);
//第三問
addedge(1,1+n,INF);
addedge(n,n+n,INF);
addedge(s,1,INF);
if(f[n]==max) addedge(n+n,t,INF);
while(BFS()){
memset(cur,0,sizeof(cur));
maxflow+=DFS(s,INF);
}
printf("%d\n",maxflow);
return 0;
}