Yahoo Programming Contest 2019
A - Anti-Adjacency
签.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int n, k; 7 while (scanf("%d%d", &n, &k) != EOF) 8 { 9 int remind = (n + 1) / 2; 10 puts(k <= remind ? "YES" : "NO"); 11 } 12 return 0; 13 }
B - Path
签.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; int x, y, Max; vector <int> G[5]; void DFS(int u, int fa, int deep) { Max = max(Max, deep); for (auto v : G[u]) if (v != fa) DFS(v, u, deep + 1); } int main() { while (scanf("%d%d", &x, &y) != EOF) { for (int i = 1; i <= 4; ++i) G[i].clear(); G[x].push_back(y); G[y].push_back(x); for (int i = 1; i <= 2; ++i) { scanf("%d%d", &x, &y); G[x].push_back(y); G[y].push_back(x); } Max = 0; DFS(1, 1, 1); puts(Max == 4 ? "YES" : "NO"); } return 0; }
C - When I hit my pocket...
签.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 int k, a, b; 6 7 int main() 8 { 9 while (scanf("%d%d%d", &k, &a, &b) != EOF) 10 { 11 if (a >= (b - 2) || k < a) printf("%d\n", k + 1); 12 else 13 { 14 k -= a - 1; 15 int Loop = k / 2; 16 if (Loop == 0) printf("%d\n", a + k % 2); 17 else printf("%lld\n", 1ll * b + 1ll * (Loop - 1) * (b - a) + k % 2); 18 } 19 } 20 return 0; 21 }
D - Ears
Upsolved.
题意:
有一个人在一维线段上走
$每次经过i - 0.5 i 是整数, 就在第i个位置放一颗石头$
$并且起点和终点必须是整数点,只能在整数点向$
$这样一条合法的路径可以用一个石头序列来表示$
$现在给出每个位置的石头数量,这个可能是不合法的$
$但是可以在某个位置放置或者移除一块石头$
$求最少的操作次数使得石头序列合法$
思路:
我们考虑一个合法的石头序列肯定是
零碎 + 偶数 + 奇数 + 偶数 + 零碎
这样五部分构成,并且至少有一部分的个数不为$0$
我们考虑$dp[i][j] 表示第i个位置,当前点位于第j个状态$
$转移即可$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 int n, a[N]; 7 ll f[N][5]; 8 9 int main() 10 { 11 while (scanf("%d", &n) != EOF) 12 { 13 for (int i = 1; i <= n; ++i) scanf("%d", a + i); 14 memset(f, 0x3f, sizeof f); 15 for (int i = 0; i < 5; ++i) f[0][i] = 0; 16 // 0 左边零碎 17 // 1 左边偶数 18 // 2 奇数 19 // 3 右边偶数 20 // 4 右边零碎 21 for (int i = 1; i <= n; ++i) 22 { 23 ll Min = (ll)1e18, cost; 24 cost = a[i]; 25 Min = min(Min, f[i - 1][0]); 26 f[i][0] = Min + cost; 27 28 cost = a[i] == 0 ? 2 : (a[i] % 2); 29 Min = min(Min, f[i - 1][1]); 30 f[i][1] = Min + cost; 31 32 cost = a[i] == 0 ? 1 : (a[i] % 2 == 0); 33 Min = min(Min, f[i - 1][2]); 34 f[i][2] = Min + cost; 35 36 cost = a[i] == 0 ? 2 : (a[i] % 2); 37 Min = min(Min, f[i - 1][3]); 38 f[i][3] = Min + cost; 39 40 cost = a[i]; 41 Min = min(Min, f[i - 1][4]); 42 f[i][4] = Min + cost; 43 } 44 ll res = (ll)1e18; 45 for (int i = 0; i < 5; ++i) 46 res = min(res, f[n][i]); 47 printf("%lld\n", res); 48 } 49 return 0; 50 }
F - Pass
Upsolved.
题意:
$有n个人,每个人手中有两个球,球有红蓝两种$
$0代表手中有两个红球$
$1代表手中有一红一蓝$
$2代表手中有两个蓝球$
$每次每个编号为非1的人,如果手中还有球,那就挑一个球给前面的人$
$编号为1的人,如果手中有球,就将球接到结果集中$
$最后结果集为一个长度为2 \cdot n的字符串,r 代表红球, b代表蓝球$
$求结果集的方案数$
思路:
$dp[i][j] 表示到第i位,已经安排了j个红球的方案数$
$那只需要考虑i + 1这一位能不能放红球后者能不能放蓝球,就能确定转移$
$我们考虑第i个人的球至少要放在第i位或者之后$
$因为传递需要一个过程$
$用前缀和维护一下,就可以知道前i位最多放多少红球, 转移即可$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 4010 6 const ll MOD = (ll)998244353; 7 int n; 8 char s[N]; 9 ll f[N][N]; 10 int a[N], b[N]; 11 12 template <class T> 13 void add(T &x, T &y) { x += y; if (x >= MOD) x -= MOD; } 14 15 int main() 16 { 17 while (scanf("%s", s + 1) != EOF) 18 { 19 n = strlen(s + 1); 20 for (int i = 1; i <= n; ++i) a[i] = a[i - 1] + 2 - (s[i] - '0'), b[i] = b[i - 1] + s[i] - '0'; 21 memset(f, 0, sizeof f); 22 f[0][0] = 1; 23 for (int i = 0; i <= 2 * n; ++i) 24 for (int j = 0; j <= i; ++j) if (f[i][j]) 25 { 26 if (a[min(i + 1, n)] > j) add(f[i + 1][j + 1], f[i][j]); 27 if (b[min(i + 1, n)] > i - j) add(f[i + 1][j], f[i][j]); 28 } 29 printf("%lld\n", f[2 * n][a[n]]); 30 } 31 return 0; 32 }