寒假训练2024/1/29

2024/1/29

codeforce 921

A - We Got Everything Covered!

题意:

输出一个字符串,使得所有前k个字母表示的长度为n的字符串都是这个字符串的子串。

思路:

这个题稍微猜想一下就可以,其实是个傻瓜题(为C题铺垫)。

把前k个字母,输出n遍就行。

#include <bits/stdc++.h>
using namespace std;

#define int long long

void solve() {
	int n, k;
	cin >> n >> k;

	string s = "";
	for (int i = 0; i < k; i++) {
		s += 'a' + i;
	}

	for (int i = 0; i < n; i++) {
		cout << s;
	}
	cout << endl;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;

	while(T--) {
		solve();
	}

	return 0;
}

B - A Balanced 问题集et?

题意:

给x和n,把x拆成n个数,使得这n个数的gcd最大。

思路:

gcd最大,那就要使得这几个数拆分的尽可能均匀,那么就要找第一个大于等于n的x的因子。

划重点 : 求所有因子的数量的复杂度是根号n,

#include <bits/stdc++.h>
using namespace std;

#define int long long

void solve() {
	int x, n;
	cin >> x >> n;

	set<int>s;
	for (int i = 1; i * i <= x; i++) {
		if(x % i == 0) {
			s.insert(i);
			s.insert(x / i);
		}
	}

	int loc = x / n;
	auto res = s.lower_bound(loc);
	if(*res != loc) {
		res--;
	}

	cout << *res << endl;	
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;

	while(T--) {
		solve();
	}

	return 0;
}

C - Did We Get Everything Covered?

题意:

与A题有关系,A是让你给出这个字符串,C是让你验证这个字符串,如果不是就给出反例。

题目描述的很复杂,简单来说就是给一个字符串,看是不是所有的由前k个字母组成的长度为n的字符串都能在给定的字符串中找到。

思路:

由A题我们得知,这个字符串必须有至少n块,每一块至少包含一遍前k个字符串,这样才符合条件。

验证的方法是这个,但是反例怎么举就想不到了。

我看了题解,题解很妙,对于每一块的前k个字母,开了一个数字外加一个计数器,如果计数器==n,重新计数,看是不是能记n轮。

对于反例,虽然不是很懂,但是能感觉到这样是对的。

把每一块中出现的最后一个字母拿出来,组合起来,把最后一轮没出现的字母补到后面补成n项,就是反例。

我猜测每块中取最后一个字母是保证唯一性把,因为最后一个字母能够保证在每一块中只出现了一遍。

#include <bits/stdc++.h>
using namespace std;

#define int long long
int vis[26];
void solve() {
	int n, k, m;
	cin >> n >> k >> m;

	string s;
	cin >> s;

	int cnt = 0;
	int res = 0;
	string ans = "";

	memset(vis, 0, sizeof(vis));
	for (auto c : s) {
		cnt += (!vis[c - 'a']);
		vis[c - 'a'] = 1;

		if(cnt == k) {
			cnt = 0;
			ans += c;
			if(++res == n) {
				break;
			}
			memset(vis, 0, sizeof(vis));
		}
	}
	
	if(res == n) {
		cout << "YES\n";
	}
	else {
		for (int i = 0; i < k; i++) {
			if(!vis[i]) {
				cout << "NO\n";
				while(ans.size() < n) {
					ans += (char)('a' + i);
				}
				cout << ans << endl;
				return ;
			}
		}
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;

	while(T--) {
		solve();
	}

	return 0;
}

D. Good Trip

概率dp以后补

uva116

题意:

给一张图,你需要从第一列的某一个点开始走,走到最后一列,你的走法只能是右,右上和右下三种。要求路径和最小,如果有多条路径,求字典序最小的那个。

思路:

这个题是在简单的dp(求最小路径和)的基础上加上了路径。

很容易想到,我们设置前驱节点和值两个关键字,我们在dp更新路径的时候更新前驱节点就行。

我刚开始用的是$pair<int, int>[r][c]$ 的方式存储,存的是第一关键字存的是前驱节点,第二关键字是从第一列的某个点到这个点的最小路径和。

最后我们遍历最后一列找到最小路径和,沿着前驱节点向前遍历节点输出即可。

但是这样有几个问题,首先最小路径和可能有几个相同,我们必须都找到,然后比较最小字典序,输出。

交上去wa了。

找到测试样例我发现,我们这样找到的路径不一定是最小的,因为我们只关注了前驱节点而没有关注整条路的字典序,比如123和321在更新第四个节点的时候如果只关注前驱节点就会选择较大字典序的321,而123会被忽略。

最后改用$pair<string, int>[r][c]$ 的方式存,因为题目给的数据是10行100列,对于每一列我们都有一个前驱节点,前驱节点的范围是行数也就是10个,我们完全可以用数字字符0-9存储,最后转化输出,因为用字符串记录路径比价容易比较,但是如果用vector存储的话还要另外写比较i函数。

最后我们整理好细节,再调试一下就ok了。

#include <bits/stdc++.h>
using namespace std;

#define int long long
int arr[10][100];

void solve(int r, int c) {
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			cin >> arr[i][j];
		}
	}

	pair<string, int>dp[10][100];
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			dp[i][j].second = 1e18;
			dp[i][j].first = "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999";
		}
	}

	for (int i = 0; i < r; i++) {
		dp[i][0].second = arr[i][0];
		dp[i][0].first = "";
	}

	for (int j = 0; j < c - 1; j++) {
		for (int i = 0; i < r; i++) {
			for (int k = -1; k < 2; k++) {
				int ne = i + k;
				if(ne < 0) {
					ne = r - 1;
				}
				else if(ne >= r) {
					ne = 0;
				}
				if(dp[i][j].second + arr[ne][j + 1] < dp[ne][j + 1].second || (dp[i][j].second + arr[ne][j + 1] == dp[ne][j + 1].second && dp[i][j].first + (char)(i + '0') < dp[ne][j + 1].first)) {
					dp[ne][j + 1].second = dp[i][j].second + arr[ne][j + 1];
					dp[ne][j + 1].first = dp[i][j].first + (char)('0' + i);

				}
			}
		}
	}

	int minm = 1e18;
	for (int i = 0; i < r; i++) {
		if(dp[i][c - 1].second < minm) {
			minm = dp[i][c - 1].second;
		}
	}
	vector<int>num_v;
	for (int i = 0; i < r; i++) {
		if(dp[i][c - 1].second == minm) {
			num_v.push_back(i);
		}
	}

	auto f =[&](int num) {
		string s = "";
		int cnt = c - 1;
		int nr = num;
		for (; cnt >= 0; cnt--) {
			s.push_back(nr + '0');
			nr = dp[nr][cnt].first.back() - '0';
		}
		reverse(s.begin(), s.end());
		return s;
	};

	vector<string>ans;
	for (auto it : num_v) {
		ans.push_back(f(it));
	}
	sort(ans.begin(), ans.end());
	bool flag = 0;
	for (auto c : ans[0]) {
		if(flag) {
			cout << " ";
		}
		else {
			flag = 1;
		}
		cout << c - '0' + 1;
	}
	
	cout << endl << minm << endl;

}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int r, c;

	while(cin >> r >> c) {
		solve(r, c);
	}

	return 0;
}

posted @ 2024-01-31 21:58  contiguous  阅读(33)  评论(0编辑  收藏  举报