題目描述
給定正整數序列 ,以下遞增子序列均爲非嚴格遞增。
- 計算其最長遞增子序列的長度。
- 計算從給定的序列中最多可取出多少個長度爲的遞增子序列。
- 如果允許在取出的序列中多次使用和,則從給定序列中最多可取出多少個長度爲的遞增子序列。
輸入格式
文件第行有個正整數 ,表示給定序列的長度。接下來的行有個正整數~ 。
輸出格式
第行是最長遞增子序列的長度 。第行是可取出的長度爲的遞增子序列個數。第行是允許在取出的序列中多次使用和時可取出的長度爲的遞增子序列個數。
樣例輸入
4
3 6 2 5
樣例輸出
2
2
3
分析:
在建模前,需要預處理得到:表示以結尾可以得到的最長遞增子序列長度,同時可得第一問的答案——最長遞增子序列的長度。
對每一個點進行拆點,如點拆分爲(入點爲,出點爲),容量爲1,表示僅可選一次;
對於的點,說明可以作爲最長遞增子序列的起點,連邊,容量爲;
對於的點,說明可以作爲最長遞增子序列的終點,連邊,容量爲;
而對於點之間,若,說明可以接在之後,連邊,容量爲1。
跑最大流,即爲第二問答案。
對於第三問,只需將點1和點n的拆點容量改爲即可,但要注意特判的情況。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 10;
int n, s = 0, t = maxn - 1;
int a[maxn];
int head[maxn], cnt;
struct edge {
int w; //邊的流量(殘量)
int to; //該邊通向的結點v
int next; //點u鄰接表的下一條邊
} e[maxn];
void add_edge(int u, int v, int w) //添加一條u->v,最大容量爲w的邊
{
//建立正向邊
e[cnt].w = w;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
cnt++;
//建立反向邊
e[cnt].w = 0; //有些圖是需要建立雙向邊(例如求最小割時),則反向邊的初始殘量不爲0
e[cnt].to = u;
e[cnt].next = head[v];
head[v] = cnt;
cnt++;
}
int dis[maxn]; // dis數組記錄層次
bool bfs() //利用BFS建立分成圖,從而可以多次DFS增廣
{
memset(dis, -1, sizeof(dis)); //初始化dis數組
queue<int> q;
q.push(s);
dis[s] = 0; //源點層次爲0
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (e[i].w > 0 && dis[v] == -1) //可達&&未分層
{
dis[v] = dis[u] + 1; //分層
if (v == t) //若到達匯點,則分層結束,返回true
return true;
q.push(v);
}
}
}
return false; //運行到此處,說明匯點已不可達,返回false
}
int cur[maxn]; //弧優化:cur數組用於記錄上一次DFS增廣時u已經增廣到第幾條邊,從而優化時間
int dfs(int u, int flow) // flow代表流入u點的最大流量
{
if (u == t)
return flow; //到達匯點,直接返回flow
for (int &i = cur[u]; i != -1; i = e[i].next) { //注意i前面用&引用,這樣就可以直接改變cur[u]
int v = e[i].to;
if (dis[v] == dis[u] + 1 && e[i].w > 0) // v爲u的下一層&&可達
{
int k = dfs(v, min(flow, e[i].w));
if (k > 0) {
e[i].w -= k; //正向邊-=k
e[i ^ 1].w += k; //反向邊+=k
return k;
}
}
}
return 0; //無法繼續增廣,返回0
}
int dinic() {
int ans = 0; //記錄總流量
while (bfs()) //分層
{
for (int i = 0; i < maxn; i++) //初始化cur數組,即將head數組賦給cur數組
cur[i] = head[i];
while (int k = dfs(s, INF)) //增廣
ans += k;
}
return ans;
}
int dp[1010], max_s;
void init() {
memset(head, -1, sizeof(head));
cnt = 0;
}
int main() {
init();
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
dp[i] = 1;
for (int j = 1; j < i; j++) {
if (a[j] <= a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
max_s = max(max_s, dp[i]);
}
printf("%d\n", max_s);
for (int i = 1; i <= n; i++) {
add_edge(i, n + i, 1);
if (dp[i] == 1)
add_edge(s, i, INF);
if (dp[i] == max_s)
add_edge(n + i, t, INF);
for (int j = 1; j < i; j++) {
if (a[j] <= a[i] && dp[j] + 1 == dp[i])
add_edge(n + j, i, 1);
}
}
printf("%d\n", dinic());
init();
for (int i = 1; i <= n; i++) {
if (i == 1 || i == n)
add_edge(i, n + i, INF);
else
add_edge(i, n + i, 1);
if (dp[i] == 1)
add_edge(s, i, INF);
if (dp[i] == max_s)
add_edge(n + i, t, INF);
for (int j = 1; j < i; j++) {
if (a[j] <= a[i] && dp[j] + 1 == dp[i])
add_edge(n + j, i, 1);
}
}
if (max_s == 1)
printf("%d\n", n); //特判
else
printf("%d\n", dinic());
return 0;
}