「比赛题解」AtCoder Beginner Contest 184 题解
\(\text{不一样的阅读体验}\)
ABC184 简要题解
A
输入 \(a,b,c,d\),输出 \(a\times c - b\times d\)
//Code by do_while_true
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define re register
//#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w = 1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
r = (r << 3) + (r << 1) + (ch ^ 48);
ch = getchar();
}
return w ? ~r + 1 : r;
}
//#undef int
int a, b, c, d;
signed main() {
a = read(); b = read(); c = read(); d = read();
printf("%d\n", a*d - b*c);
return 0;
}
B
根据题意模拟,每一次读入判断要加还是减。
//Code by do_while_true
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define re register
//#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w = 1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
r = (r << 3) + (r << 1) + (ch ^ 48);
ch = getchar();
}
return w ? ~r + 1 : r;
}
//#undef int
const int N = 2e5 + 10;
int n, ans;
char ch[N + 10];
signed main() {
n = read(); ans = read();
scanf("%s", ch + 1);
for(int i = 1; i <= n; ++i) {
if(ch[i] == 'o') ++ans;
if(ch[i] == 'x' && ans) --ans;
}
printf("%d\n", ans);
return 0;
}
C
分类讨论一下怎么走最优即可。
//Code by do_while_true
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define re register
//#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w = 1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
r = (r << 3) + (r << 1) + (ch ^ 48);
ch = getchar();
}
return w ? ~r + 1 : r;
}
//#undef int
const int N = 2e5 + 10;
int a, b, c, d;
signed main() {
a = read(); b = read(); c = read(); d = read();
if(a == c && b == d) {
puts("0");
return 0;
}
if(a + b == c + d || a - b == c - d || Abs(a - c) + Abs(b - d) <= 3) {
puts("1");
return 0;
}
if(((d-c) & 1) == ((b-a) & 1)) {
puts("2");
return 0;
}
if(Abs(c + d - a - b) <= 3) {
puts("2");
return 0;
}
if(Abs(Abs(a-b) - Abs(c-d)) <= 3) {
puts("2");
return 0;
}
if(Abs(a - c) + Abs(b - d) <= 6) {
puts("2");
return 0;
}
puts("3");
return 0;
}
下面才是正文(不会吧不会吧,不会真的有人ABC的前三题都要看题解的吗)
D
看到 \(100\) 的数据范围,考虑 \(n^3\) 的做法,显然是 \(dp\),\(f_{i,j,k}\) 代表第一种选了 \(i\) 个,第二种选了 \(j\) 个,第三种选了 \(k\) 个的概率。
因为知道了选到 \(i,j,k\),代价就是 \((i+j+k-a-b-c)\),期望就能根据概率推出来,而概率比期望更好 \(dp\),所以选择概率作为状态。
这个时候就很好算了,显然的,可以从选第一种,选第二种,选第三种,这三种不同的选择方案转移而来,统计一下 \(i,j,k\) 其中有 \(100\) 的期望加起来即可。
转移方程见代码。
//Code by do_while_true
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define ld double
#define re register
//#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w = 1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
r = (r << 3) + (r << 1) + (ch ^ 48);
ch = getchar();
}
return w ? ~r + 1 : r;
}
//#undef int
const int N = 110;
int a, b, c, d;
ld f[N][N][N], ans;
signed main() {
a = read(); b = read(); c = read();
for(int i = 1; i <= 100; ++i)
for(int j = 1; j <= 100; ++j)
for(int k = 1; k <= 100; ++k)
f[i][j][k] = -1;
f[a][b][c] = 1;
for(int i = a; i <= 100; ++i)
for(int j = b; j <= 100; ++j)
for(int k = c; k <= 100; ++k) {
if(i == a && j == b && k == c) continue;
f[i][j][k] = 0;
if(i > a && j != 100 && k != 100 && f[i-1][j][k] != -1) f[i][j][k] += f[i-1][j][k] * (i-1) / (i-1+j+k);
if(j > b && i != 100 && k != 100 && f[i][j-1][k] != -1) f[i][j][k] += f[i][j-1][k] * (j-1) / (i+j-1+k);
if(k > c && i != 100 && j != 100 && f[i][j][k-1] != -1) f[i][j][k] += f[i][j][k-1] * (k-1) / (i+j+k-1);
if(i == 100 || j == 100 || k == 100) ans += (i - a + j - b + k - c) * f[i][j][k];
}
printf("%.6lf\n", ans);
return 0;
}
E
直接广搜即可,有一个小优化就是每种类型的传送门只需要遍历一遍就行了,因为第一遍过后这一种的全部的传送门都会变成入过队的点。因为和这道题不一样,不是强制传送,所以每个点只入队的广搜是正确的。时间复杂度为 \(\mathcal{O}(HW)\)
//Code by do_while_true
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#define ll long long
#define ld double
#define re register
#define pb push_back
//#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w = 1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
r = (r << 3) + (r << 1) + (ch ^ 48);
ch = getchar();
}
return w ? ~r + 1 : r;
}
//#undef int
const int N = 2010;
const int INF = 0x3f3f3f3f;
int n, m, sx, sy, f[N][N];
char ch[N][N];
bool vis[N][N];
bool viss[27];
struct Point {
int x, y;
Point(int xx, int yy) { x = xx; y = yy; }
};
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
std::queue<Point>q;
std::vector<Point>vec[27];
signed main() {
n = read(); m = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) {
std::cin >> ch[i][j];
if(ch[i][j] == 'S') {
q.push(Point(i, j));
vis[i][j] = 1;
}
if(ch[i][j] >= 'a' && ch[i][j] <= 'z') vec[ch[i][j]-'a'].pb(Point(i, j));
if(ch[i][j] == 'G') {
sx = i;
sy = j;
}
}
while(!q.empty()) {
Point now = q.front(); q.pop();
int x = now.x, y= now.y;
if(f[sx][sy]) break;
for(int i = 0; i < 4; ++i) {
int tx = x + dx[i], ty = y + dy[i];
if(ch[tx][ty] == '#' || vis[tx][ty] || tx < 1 || ty < 1 || tx > n || ty > m) continue;
q.push(Point(tx, ty));
vis[tx][ty] = 1;
f[tx][ty] = f[x][y] + 1;
}
if(ch[x][y] >= 'a' && ch[x][y] <= 'z') {
int pos = ch[x][y] - 'a', siz = vec[pos].size();
if(viss[pos]) continue;
viss[pos] = 1;
for(int i = 0; i < siz; ++i) {
int tx = vec[pos][i].x, ty = vec[pos][i].y;
if(!vis[tx][ty]) {
q.push(Point(tx, ty));
vis[tx][ty] = 1;
f[tx][ty] = f[x][y] + 1;
}
}
}
}
printf("%d\n", f[sx][sy] == 0 ? -1 : f[sx][sy]);
}
F
第一眼:这玩意不是NPC吗怎么可能解决。
看到 \(N\leq 40\),哦,折半搜索啊。
把集合分成两个同等大小的集合,对其中一个集合进行爆搜/状压求出每一种子集的总和,排一下序。
对另一个集合进行爆搜/状压每一种子集的总和,在第一个集合的每一个子集的总和中二分出最优的方案,更新答案。
时间复杂度 \(\mathcal{O}(2^{\frac{N}{2}}\log {N})\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define re register
#define int long long
inline int Max(int x, int y) { return x > y ? x : y; }
inline int Min(int x, int y) { return x < y ? x : y; }
inline int Abs(int x) { return x < 0 ? ~x+1 : x; }
inline int read() {
re int r = 0; re bool w = 0; re char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : w, ch = getchar();
while(ch >= '0' && ch <= '9') r = (r << 3) + (r << 1) + (ch ^ 48), ch = getchar();
return w ? ~r+1 : r;
}
#undef int
const int N = 41;
int n, cnt1, cnt2, cnt;
ll T, a[N], b[N], c[N], ans;
ll d[2100000];
signed main() {
n = read(); T = read();
for(int i = 1; i <= n; ++i) a[i] = read();
cnt1 = n / 2; cnt2 = n - cnt1;
for(int i = 1; i <= cnt1; ++i) b[i] = a[i];
for(int i = 1; i <= cnt2; ++i) c[i] = a[n-i+1];
for(int i = 0; i <= (1 << cnt1) - 1; ++i) {
++cnt;
for(int j = 1; j <= cnt1; ++j)
if((1 << (j-1)) & i)
d[cnt] += b[j];
}
std::sort(d + 1, d + cnt + 1);
for(int i = 0; i <= (1 << cnt2) - 1; ++i) {
ll now = 0;
for(int j = 1; j <= cnt2; ++j)
if((1 << (j-1)) & i)
now += c[j];
if(now > T) continue;
int pos = std::upper_bound(d + 1, d + cnt + 1, T - now) - d;
--pos;
ans = Max(ans, now + d[pos]);
}
printf("%lld\n", ans);
}