\(\quad\) 一旦知道这道题是网络流,思路就很好想了。
\(\quad\) 先用 \(O(n^2)\)dp 求出 \(dp\) 和 \(s\)。在这里 \(dp_i\) 表示在 \([1,i]\) 范围中最后一个元素为 \(i\) 的最长不下降子序列长度。
for(int i = 1; i <= n; ++i)
for(int j = 0; j < i; ++j){
if(x[j] <= x[i])
dp[i] = max(dp[i], dp[j] + 1);
s = max(s, dp[i]);
}
\(\quad\) \(\mathrm {I.}\)
\(\qquad\) 直接输出 \(s\) 即可。
\(\quad\) \(\mathrm {II.}\)
\(\qquad\) \(\forall i,j\) 满足 \(i > j\) 且 \(x_i \geq x_j\),连一条从 \(r_j\) 到 \(l_i\),容量为 \(1\) 的有向边;\(\forall i\) 满足 \(dp_i = 1\),连一条从源点到 \(l_i\),容量为 \(1\) 的有向边;\(\forall i\) 满足 \(dp_i = s\),连一条从 \(r_i\) 到汇点,容量为 \(1\) 的有向边,\(\forall i\),连一条从 \(l_i\) 到 \(r_i\),容量为 \(1\) 的有向边。这个图的最大流即为答案。
\(\quad\) \(\mathrm {III.}\)
\(\qquad\) 开始把这道题看成所有元素都可以多次使用了,调了好久~直接连 \((s,l_1)\),\((l_1, r_1)\),\((l_n,r_n)\),\((r_n,t)\) 容量为 \(+\infty\) 的有向边即可。
#include <cstdio>
#include <cstring>
#include <queue>
inline int max(const int& a, const int& b){
return a > b ? a : b;
}
inline int min(const int& a, const int& b){
return a < b ? a : b;
}
const int MAXN = 5e2 + 19, INF = 0x3f3f3f3f;
struct Edge{
int to, next, c;
}edge[MAXN * MAXN << 1];
int cnt = -1, head[MAXN << 1];
inline void add(int from, int to, int c){
edge[++cnt].to = to;
edge[cnt].c = c;
edge[cnt].next = head[from];
head[from] = cnt;
}
int n, x[MAXN], s;
int dep[MAXN << 1];
int bfs(void){
std::memset(dep, 0, sizeof(dep)); dep[0] = 1;
std::queue<int>q; q.push(0);
while(!q.empty()){
int node = q.front();
for(int i = head[node]; i != -1; i = edge[i].next)
if(!dep[edge[i].to] && edge[i].c)
dep[edge[i].to] = dep[node] + 1, q.push(edge[i].to);
q.pop();
}
return dep[n + n + 1];
}
int dfs(int node, int flow){
if(node == n + n + 1 || !flow)
return flow;
int stream = 0, f;
for(int i = head[node]; i != -1; i = edge[i].next)
if(dep[edge[i].to] == dep[node] + 1 && (f = dfs(edge[i].to, min(flow, edge[i].c)))){
flow -= f, stream += f;
edge[i].c -= f, edge[i ^ 1].c += f;
if(!flow)
break;
}
return stream;
}
int dinic(void){
int flow = 0;
while(bfs())
flow += dfs(0, INF);
return flow;
}
int dp[MAXN], ans;
int main(){
std::scanf("%d", &n);
for(int i = 1; i <= n; ++i)
std::scanf("%d", x + i);
for(int i = 1; i <= n; ++i)
for(int j = 0; j < i; ++j){
if(x[j] <= x[i])
dp[i] = max(dp[i], dp[j] + 1);
s = max(s, dp[i]);
}
std::printf("%d\n", s);
std::memset(head, -1, sizeof(head));
for(int i = 1; i <= n; ++i)
add(i, i + n, 1), add(i + n, i, 0);
for(int i = 1; i <= n; ++i){
if(dp[i] == 1)
add(0, i, 1), add(i, 0, 0);
for(int j = 1; j < i; ++j)
if(dp[i] == dp[j] + 1 && x[i] >= x[j])
add(j + n, i, 1), add(i, j + n, 0);
if(dp[i] == s)
add(i + n, n + n + 1, 1), add(n + n + 1, i + n, 0);
}
std::printf("%d\n", ans = dinic());
add(0, 1, INF), add(1, 0, 0), add(1, 1 + n, INF), add(1 + n, 1, 0);
if(dp[n] == s)
add(n, n + n, INF), add(n + n, n, 0), add(n + n, n + n + 1, INF), add(n + n + 1, n + n, 0);
std::printf("%d\n", ans + dinic());
return 0;
}
\(\quad\) 这道题洛谷上有很多奇奇怪怪的数据,好烦啊。