比赛链接:

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;
}
posted on 2022-07-29 10:28  Hamine  阅读(24)  评论(0编辑  收藏  举报