2022SHCPC

2022SHCPC

作为新的起点!srds还有好多没写的,

A Another A+B Problem

Description

​ 给定一个等式,形式为 ab+cd=ef 。 对于每个数,给定一个颜色。绿色表示这个数的位置正确;紫色表示这个数在答案等式中但位置不正确;黑色表示这个数不在答案等式中,且答案等式该位置上不是该数。

Solution

​ 注意到至多只有6个未知数,而每个数有10种可能。枚举的复杂度是\(O(10^6)\),可行。

​ 具体如何判断合法?

​ 首先需要对于每一个特定的数字 i(0~9),记录它在输入等式中绿色和紫色格子里出现的次数\(num_i\)和它是否在黑色格子出现过\(ban_i\)

​ 合法的答案等式应该满足下列条件:

​ ① 对于每一个数字(0~9),设其在准答案等式中出现\(tmpnum_i\)次,那么应该满足\(tmpnum_i>=num_i\)

​ ② 对于每一个在黑色格子中出现的数字,要满足\(tmpnum_i==num_i\)

​ ③ 对于所有的位置,若该位置的颜色是紫色或黑色,那么该位置上的数不能与输入等式中的相同。

​ ④ 判断数值是否能使等式成立。

Code

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e6 + 5, M = 12;
char s1[M], s2[M], s3[M];
int s4[M],ansnum,ans[N][M];
int num[M],tmp[M],tmpnum[M];
bool ban[M];
bool judge() {
	//首先判断数目是否合法
	for (int i = 0;i < 10;++i) {
		if (ban[i] && tmpnum[i] != num[i])return 0;
		if (!ban[i] && tmpnum[i] < num[i])return 0;
	}
	//判断数所在的位置是否合法
	for (int i = 0;i < 6;++i) {
		if (s3[i] != 'G' && tmp[i] == s4[i])return 0;
	}
	//判断等式是否成立
	int x1 = tmp[0] * 10 + tmp[1];
	int x2 = tmp[2] * 10 + tmp[3];
	int x3 = tmp[4] * 10 + tmp[5];
	if (x1 + x2 != x3)return 0;
	return 1;
}
void dfs(int p) {
	if (p == 6) {
		if (judge()) {
			++ansnum;
			for (int i = 0;i < 6;++i)
				ans[ansnum][i] = tmp[i];
		}
		return;
	}
	if (s3[p] == 'G') {
		++tmpnum[s4[p]];
		tmp[p] = s4[p];
		dfs(p + 1);
		--tmpnum[s4[p]];
		return;
	}
	for (int i = 0;i < 10;++i) {
		if (ban[i] && tmpnum[i] >= num[i])continue;
		if (s3[p] != 'G' && i == s4[p])continue;
		++tmpnum[i];
		tmp[p] = i;
		dfs(p + 1);
		--tmpnum[i];
	}
}
int main() {
	cin >> s1 >> s2;
	int t[6] = { 0,1,3,4,6,7 };
	for (int i = 0;i < 6;++i) {
		s3[i] = s2[t[i]];
		s4[i] = s1[t[i]] - '0';
	}
	for (int i = 0;i < 6;++i) {
		int x = s4[i];
		if (s3[i] == 'B') ban[x] = 1;
		else ++num[x];
	}
	dfs(0);
	cout << ansnum << endl;
	for (int i = 1;i <= ansnum;++i) {
		cout << ans[i][0] << ans[i][1] << "+";
		cout << ans[i][2] << ans[i][3] << "=";
		cout << ans[i][4] << ans[i][5] << endl;
	}
	return 0;
}

E Expenditure Reduction

Description

​ 给定一个字符串和该字符串的一个子序列。

​ 求该字符串的最短的包含该子序列的子串。

Solution

​ 易得答案子串的开头与结尾字符等于给定子序列的开头和结尾字符。

​ 我们要做的就是找到结尾和开头位置相差最小的子序列。

法一:

​ 在考场里想到的是:记录一个二维数组\(pos_{i,j}\)表示从左往右第\(j\)字符\(i\)的位置,然后对每一个\(F_0\)字符往后找到一个跨度最小的子序列。从一个字符找到下一个字符的方法是:找到下一个字符的比当前位置大的最小位置(二分)。

​ 其实可以优化一下:记录一个数组\(nxt_{i,j}\)表示位置\(i\)往后的第一个\(j\)字符的位置。然后对每一个\(F_0\)字符往后找,直到得到整个子序列,并且更新答案。复杂度\(O(|S|*|\alpha|+|S|*|F|)\)

注意的细节: 更新答案不要复制字符串(会TLE)!只要记录起始地址和答案字符串长度即可。

法二:

动态规划。

\(f_{i,j}\)表示考虑到\(S\)的第\(i\)个位置,\(F\)匹配到第\(j\)个位置的字符串长度。

​ 答案:\(min(f_{i,|F|})\)

具体地说:

​ (为了转移方便,给 \(i,j\) 统一全部加上\(1\))

  1. 初始条件: \(\forall i\in [0,|S|],j=0\ f[i][j]=0\)

    \(\forall i\in[0,|S|],\forall j\in[1,|F|]\ f[i][j]=inf\)

  2. 转移: \(f[i][j]=min\{f[i][j],f[i-1][j]+1\}\)

    ​ 若\(S[i-1]==F[j-1],f[i][j]=min\{f[i][j],f[i-1][j-1]+1\}\)

  3. 答案: 找到最小的\(f[i][|F|]\),答案字符串为\(S[i-f[i][|F|+1]...S[i]\)

Code1

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e5 + 5, M = 105;;
int nxt[N][40]; //注意组成字符串的字符有 '0'~'9' 和 'a'~'z' 两个类型
char S[N], F[M];
char get(int x) {
	if (x < 26) return 'a' + x;
	return x - 26 + '0';
}
int ind(char c) {
	if (c >= 'a' && c <= 'z') return c - 'a';
	return c - '0' + 26;
}
int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> S >> F;
		int lens = strlen(S), lenf = strlen(F), anslen = lens, ansbeg = 0;
		//首先初始化nxt数组
		for (int i = 0;i < 36;++i) {
			char c = get(i);
			int nxtpos = 0;
			for (int j = lens - 1;j >= 0;--j) {
				nxt[j][i] = nxtpos;
				if (S[j] == c) nxtpos = j;
			}
		}
		//从S的每个位置开始尝试寻找F
		for (int i = 0;i < lens;++i) {
			if (S[i] != F[0])continue;
			int curpos = i;
			bool flag = 1;
			for (int j = 1;j < lenf;++j) {
				if (nxt[curpos][ind(F[j])] == 0) {
					flag = 0;
					break;
				}
				curpos = nxt[curpos][ind(F[j])];
			}
			if (!flag)continue;
			int curlen = curpos - i + 1;
			if (curlen >= anslen)continue;
             //更新答案
			anslen = curlen;  
			ansbeg = i;
		}
		for (int i = ansbeg;i < ansbeg + anslen;++i) cout << S[i];
		cout << endl;
		for (int i = 0;i < lens;++i)
			for (int j = 0;j < 36;++j)
				nxt[i][j] = 0;
	}
	return 0;
}

Code2

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e5 + 5, M = 105;;
int f[N][M];
char S[N], F[M];

int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> S >> F;
		int lens = strlen(S), lenf = strlen(F);
        //初始化
		for (int i = 0;i <= lens;++i)
			for (int j = 1;j <= lenf;++j)
				f[i][j] = 1e9;
        //dp
		for (int i = 1;i <= lens;++i) {
			for (int j = 1;j <= lenf;++j) {
				f[i][j] = min(f[i][j], f[i - 1][j] + 1);
				if (S[i - 1] == F[j - 1])
					f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1);
			}
		}
        //统计并输出答案
		int anslen = lens, ansend = lens-1;
		for (int i = 1;i <= lens;++i)
			if (anslen > f[i][lenf]) {
				anslen = f[i][lenf];
				ansend = i - 1;
			}
		for (int i = ansend - anslen + 1;i <= ansend;++i)
			printf("%c", S[i]);
		printf("\n");
	}
	return 0;
}

G Gua!

Sol

​ 判断S * (R / 60.0) * B 与 D 的大小关系即可。

H Heirloom Painting

Descirption

​ 一个圆环分为 \(n\) 个格子,共有 \(m\) 个颜色。给格子涂颜色,一次必须涂连续的 \(k\) 个格子,后涂的颜色可以覆盖先涂的颜色。所有格子初始无色,给定所有格子的预期颜色,问最少涂多少次能使每个格子涂成预期颜色。

Solution

​ 注意到最后一次涂颜色一定会得到连续 \(k\) 同样颜色的格子。由此可以得到判断能否完成任务的条件:若答案圆环没有连续 \(k\) 个一样颜色的格子,则无法完成任务;否则一定可以完成任务。

​ 从任意两个相邻的不同色的格子中间将环断开成链,扫描整条链,对于每一个长度为 \(l\) 的相同颜色区间,涂色次数为\(\lceil len/l\rceil\),累加所有区间涂色次数,\(\sum \lceil len/l\rceil\) 即为最终答案。

Code

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
const int N = 1e6 + 5;
int n, m, k, a[N << 1], ans;
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d", &n, &m, &k);
		for (int i = 1;i <= n;++i) {
			scanf("%d", &a[i]);
			a[i + n] = a[i];
		}
		int len = 1, maxlen = 1, pos = 1;
		for (int i = 2;i <= n * 2;++i) {
			if (a[i] == a[i - 1]) ++len;
			else {
				len = 1;
				if (i <= n) pos = i;
			}
			if (len > maxlen) maxlen = len;
		}
		if (len > maxlen) maxlen = len;
		if (maxlen < k) {
			printf("-1\n");
			continue;
		}
		ans = 0;
		len = 1;
		for (int i = pos + 1;i < pos + n;++i) {
			if (a[i] == a[i - 1]) ++len;
			else ans += (len + k - 1) / k, len = 1;
		}
		ans += (len + k - 1) / k;
		printf("%d\n", ans);
	}
	return 0;
}

M My University Is Better Than Yours

Description

​ 有 \(n\) 个学校,\(m\) 个排行榜。定义一个学校 \(x\) 好于 \(y\),当且仅当存在一个排行榜,\(x\) 排在 \(y\) 前面。

​ 对于每一所学校,问这所学校好于多少所学校。

Solution

​ 对于每一个排行榜,\(\forall i\in[1,n-1]\),建从 \(a_i\)\(a_{i+1}\) 的边。最后得到一个有向图。

​ 对于每一个结点,答案为以它为起点能到达的点的数目。

\(Tarjan\) 缩点得到 \(DAG\) 图。考虑本题的特殊性质,对于任意两个学校,一定有路能从其中某个通向另外一个。所以最后得到的是一条链(注意要进行一轮拓扑排序得到),可以直接统计答案。

N Nine Is Greater Than Ten

签到题,直接比较字符串大小即可。

posted @ 2022-10-09 21:49  DTTTTTTT-  阅读(59)  评论(0)    收藏  举报