比赛链接:
https://ac.nowcoder.com/acm/contest/33187
G.Link with Monotonic Subsequence
题意:
构造一个排列,让 \(max(lis(p), lds(p))\) 最小。
思路:
根据 \(Dilworth\) 定理,最小上升子序列长度为 \(\lceil \sqrt{n} \rceil\),所以构造每组长度都是 <= $ \lceil \sqrt{n} \rceil$ 的序列即可。
例如 9,可以构造 3 2 1 6 5 4 9 8 7 这个序列。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL n;
cin >> n;
LL k = ceil(sqrt(n));
for (int i = 1, j; i <= n; i = j + 1 ){
j = min(n, i + k - 1);
for (int x = j; x >= i; x -- )
cout << x << " ";
}
cout << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
J.Link with Arithmetic Progression
题意:
给定一个序列 \(a\),找到一个等差序列 \(a^{'}\) 使得 \(\sum_{i = 1}^{n} (a - a^{'})^2\) 最小,输出这个最小值。
思路:
就是一个线性回归。
直线的方程为 \(y = A * x + B\)。
\(A = \frac{\sum_{i = 1}^{n} x_i y_i - n \overline x \overline y}{\sum_{i = 1}^{n} x_{i}^2 - n (\overline x)^2}\)
\(B = \overline y - A \overline x\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define double long double
void solve(){
LL n;
cin >> n;
double sumx = 0, sumy = 0, mula = 0, mulb = 0;
vector <LL> y(n + 1);
for (LL x = 1; x <= n; x ++ ){ //注意要 long long
cin >> y[x];
sumx += x;
sumy += y[x];
mula += x * y[x];
mulb += x * x;
}
double A = (mula - sumx * sumy / n) / (mulb - sumx * sumx / n);
double B = sumy / n - A * sumx / n;
double ans = 0;
for (LL x = 1; x <= n; x ++ )
ans += (y[x] - A * x - B) * (y[x] - A * x - B);
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cout << fixed << setprecision(20);
LL T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
K.Link with Bracket Sequence I
题意:
已知一个长为 \(n\) 的括号序列 \(a\),它是从一个长为 \(m\) 的括号序列 \(b\) 中取出来的,求序列 \(b\) 有多少种可能。
思路:
定义 \(dp[i][j][k]\) 表示序列前 \(i\) 项,与 \(a\) 序列的 \(lcs\) 为 \(j\),左括号比右括号多 \(k\) 个的序列的数量。
如果这一位是放左括号,那么不论什么情况下都是可以放的,所以直接加上。
如果是放右括号,只有当左括号比右括号多的时候才能放。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 210, mod = 1e9 + 7;
LL dp[N][N][N];
void solve(){
LL n, m;
cin >> n >> m;
string s;
cin >> s;
s = "+" + s;
for (int i = 0; i <= m; i ++ )
for (int j = 0; j <= n; j ++ )
for (int k = 0; k <= m; k ++ )
dp[i][j][k] = 0;
dp[0][0][0] = 1;
for (int i = 0; i <= m; i ++ ){
for (int j = 0; j <= n; j ++ ){
for (int k = 0; k <= m; k ++ ){
int L = j + (s[j + 1] == '(');
int R = j + (s[j + 1] == ')');
dp[i + 1][L][k + 1] = (dp[i + 1][L][k + 1] + dp[i][j][k]) % mod;
if (k) dp[i + 1][R][k - 1] = (dp[i + 1][R][k - 1] + dp[i][j][k]) % mod;
}
}
}
cout << dp[m][n][0] << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
L.Link with Level Editor I
题意:
有 \(n\) 个关卡,每个关卡有 \(m\) 个世界,以及 \(l\) 条单向道路。在每个关卡,可以先选择留在当前世界或者选择一条道路走向其它世界,然后就会被传送到下一个关卡,问最少经过几个关卡,能从第一个世界到第 \(m\) 个世界。
思路:
定义 \(dp[i][j]\) 表示最晚从第几个关卡出发能在第 \(i\) 个关卡时,到达第 \(j\) 个世界。
如果一条道路为从 \(u\) 到达 \(v\),容易得到转移方程,\(dp[i][v] = max(dp[i - 1][v], dp[i - 1][u])\)。
空间复杂度为 \(O(n * m)\),可以通过滚动数组去优化。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL n, m;
cin >> n >> m;
vector <LL> f(m + 1, -1);
f[1] = 0;
LL ans = n + 1;
for (int i = 1; i <= n; i ++ ){
vector <LL> g = f;
g[1] = i; //到达第 1 个世界最晚可以从当前这个关卡出发
LL l;
cin >> l;
for (int j = 1; j <= l; j ++ ){
LL u, v;
cin >> u >> v;
g[v] = max(g[v], f[u]);
}
if (g[m] != -1) ans = min(ans, i - g[m]);
f = g;
}
if (ans > n) cout << "-1\n";
else cout << ans << "\n";
return 0;
}