A.翻硬币
题目中已给出硬币的初始状态与末状态,且已经保证可以到达最终状态,那么找到每个不一样的硬币,把它翻过来即可,实现方法暴力模拟即可。
考察范围:字符串,模拟
string s1, s2;
cin >> s1 >> s2;
int n = s1.size(), ans = 0;;
for (int i = 0; i < n; i++) {
if (s1[i] != s2[i]) {
if (s1[i + 1] == '*' )s1[i + 1] = 'o';
else s1[i + 1] = '*';
ans++;
}
}
cout << ans;
B.移动距离
题目中已经告诉了楼层的排列方式,那么我们就可以根据编号来确定楼层的坐标,进而得到距离差。在确定坐标时,需要注意奇数行排列时正常的,而偶数列是倒着排列的,分情况讨论即可。
考察范围:模拟
int w, n, m;
cin >> w >> n >> m;
int x1 = (n + w - 1) / w + 1, y1 = n % (w + w);
int x2 = (m + w - 1) / w + 1, y2 = m % (w + w);
if (y1 == 0) y1 = 1;
if (y2 == 0) y2 = 1;
if (y1 > w) y1 = w + w - y1 + 1;
if (y2 > w) y2 = w + w - y2 + 1;
int ans = abs(y1 - y2) + abs(x1 - x2);
cout << ans;
C.日期问题
题目中已告知仅有三种情况,年月日、月日年、日月年,根据给出的字符串,我们可以得到三个数字,然后把数字带入到上述三种情况,观察是否合适即可,约束条件为月份的数字是1-12,天数是1-(28-31),特别对闰年进行判断即可,最后再对每个答案进行排序,时间早的在前。分析到这里发现,需要的约束条件过多,既需要对三个数字来回调换,又需要判断是否符合条件,根据名言“正难则反”(bushi),我们可以倒过来思考,不再根据题目去寻找答案,而是枚举答案判断是否符合标准。
注意,在使用cin,cout时发现输入输出过于复杂,可以选择使用scanf,printf,可以省去很多麻烦。
考察范围:枚举
int d[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int y) {
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
signed main() {
int a, b, c;
scanf("%d/%d/%d", &a, &b, &c);
for (int year = 1960; year <= 2059; year++) {
d[2] = (check(year) ? 29 : 28);
for (int month = 1; month <= 12; month++) {
for (int day = 1; day <= d[month]; day++) {
if (a == year % 100 && b == month && c == day) {
printf("%d-%02d-%02d\n", year, month, day);
} else if (a == month && b == day && c == year % 100) {
printf("%d-%02d-%02d\n", year, month, day);
} else if (a == day && b == month && c == year % 100) {
printf("%d-%02d-%02d\n", year, month, day);
}
}
}
}
return 0;
}
D.时间显示
简单的大数字取余运算,注意数据范围,开启long long即可。
考察范围:模拟
#define ing long long
int n;
cin >> n;
n /= 1000;
int t1 = (n / 3600) % 24;
int t2 = ((n % 3600) / 60) % 60;
int t3 = n % 60;
printf("%02lld:%02lld:%02lld", t1, t2, t3);
E.冶炼金属
思维能力的锻炼题目,没有做出来的同学该刷题了哦~
根据每组给出的数据,我们可以计算出对于每组的最小V和最大V,然后根据每组的数据进行整体的最大最小取舍即可
注意数据范围,开启long long(建议同学们一直开着long long,因为开启而出错的情况很少,但因为没有开启而出错的情况很多)
考察范围:模拟
#define int long long
int T;
cin >> T;
int maxx = 0x3f3f3f3f, minn = 0;
while (T--) {
int a, b;
cin >> a >> b;
int r = a / b, l = a / (b + 1) + 1;
maxx = min(maxx, r);
minn = max(minn, l);
}
cout << minn << ' ' << maxx;
F.砝码称重
典型的背包问题,不了解背包问题的同学可以百度一下或是查看寒假中的视频文件。
大体思路:
对于f【i】【j】,定义为拿去 i 个砝码,所占的背包容量为 j,那么对于每个拿取的砝码,所影响的则是所有当前容量 j 的背包和 j +v【i】与 j - v【i】,而在枚举当前砝码时无法得知下一个砝码的状态,故而选择向上选择,判断之前的状态中是否有合适的即可。
实现过程:
那怎么写双重循环呢?外层循环很显然是枚举每个砝码,即将 i 从1枚举到 n,但内层循环呢?其实也很简单,因为我们之前定义了 j 为枚举质量,所以我们可以将 j 从1开始枚举,枚举到所有砝码质量总和(因为质量最大就是砝码质量总和)。
因此,易推得 f【i】【j】 如何转移:
如果枚举到当前的砝码不放,那么对于枚举到的每一个质量,其情况都与枚举到上个砝码时的情况相同,且此方案显然是否可行取决于上一个方案是否可行。 即 f【i】【j】=f【i-1】【j】
如果之前一个砝码也没放,现在枚举到的砝码是第一个放的(即当前枚举到的 j 就是当前枚举到的砝码质量),那么这种方案显然可行,因此 f【i】【j】=1(j=w【i】)
另外的情况,我们需要分类讨论:
第一种情况: 将当前枚举到的砝码放置在右盘,右盘质量为 �j,那么显然还没有放置时右盘质量为 ∣ j − W【i】 ∣。为什么加绝对值,是因为上一个砝码有可能放在左盘或右盘(这里枚举 j 为右盘质量),所以只要上一个砝码能称出 ∣ j − W【i】 ∣ 的质量,当前砝码就可以称出 j 的质量。 即当 f【i - 1】【| j - w【i】|】=1时,f【i】【j】=1。
第二种情况: 将当前枚举的砝码放在左盘,那么类似地,当 f【i-1】【j+w【i】】=1时,f【i】【j】=1。
const int N = 1e5 + 10;
bool f[110][N];
int v[110];
int n, sum = 0;
cin >> n;
for (int i = 1; i <= n; i++) cin >> v[i], sum += v[i];
for (int i = 1; i <= n; i++) {
for (int j = sum; j > 0; j--) {
if (j == v[i]) f[i][j] = 1;
else if (f[i - 1][j] == 1) f[i][j] = 1;
else if (f[i - 1][j + v[i]] == 1) f[i][j] = 1;
else if (f[i - 1][abs(j - v[i])] == 1) f[i][j] = 1;
}
}
int ans = 0;
for (int i = 1; i <= sum; i++) if (f[n][i] == 1) ans++;
cout << ans;
G.危险系数
dfs类型问题,同样的,未接触过的同学自行百度或查阅视频,或是询问学长。
求出起点u到重点v之间的每个路径,统计路径数量与每个点被经过的次数,如果一个点被经过的次数与路径总数相等,那么该点为关键点。
使用前向星存储或vector存储都可以,根据自己擅长哪一个来选择即可。
const int N = 4010;
int e[N], ne[N], h[N], idx;
int num[N];
bool st[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int ans, endd;
int n, m;
void dfs(int u) {
if (u == endd) {
ans++;
for (int i = 1; i <= n; i++) {
if (st[i] == 1) num[i]++;
}
} else {
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (st[j] == 1) continue;
st[j] = 1;
dfs(j);
st[j] = 0;
}
}
}
signed main() {
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
add(x, y), add(y, x);
}
int u;
cin >> u >> endd;
dfs(u);
if (ans > 0) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (num[i] == ans) cnt++;
}
cout << cnt - 1;
} else cout << "-1";
return 0;
}
H.扑克牌
题意很简单,但是相信大家都是对如何求组合数C(n,m)这方面产生的问题,那么就不过多的赘述了,直接看代码吧。
这里采用的是二维数组杨辉三角求组合数,可以应对大多数组合数问题,大家也可以自行百度查询更多的相关知识。
const int N = 1e4 + 10;
int a[N][110];
int n, m;
cin >> n >> m;
a[0][0] = 1;
for (int i = 1; i <= 10000; i++) {
for (int j = 0; j <= 100; j++) {
a[i][j] = (a[i - 1][j - 1] + a[i - 1][j]) % 10007;
}
}
int ans = 1;
for (int i = 1; i <= m; i++) {
int x;
cin >> x;
ans = ans * a[n][x] % 10007;
n -= x;
}
cout << ans;
到这里本场训练赛就已经结束啦,对于A-E题均为锻炼思维的题目,如果出现做错或没有想到的情况可以反思一下自己所出现的问题,然后,多刷题!其中,C题很麻烦对吧,我也是找了很久才找到的这样的题,正着做当然也能做出来,但是很麻烦,希望大家记住这句话 “正难则反”。
对于F-H题则为算法题,考察对知识内容的掌握,如果出现做错或没有想到的情况就赶快去补一下该算法吧,相信大家一定可以的,特别的,H题为杨辉三角,高中的噩梦?(bushi)现在我们可以用代码实现啦,要记住。
总之,算法竞赛重要的就是知识储备和刷题,最重要的则是思维的训练,如果想不到思路,那就快去刷题吧!