Codeforces Round #693 (Div. 3) 题解
文章目录
A. Cards for Friends
-
题意
给你一个 h × w h\times w h×w的卡片,如果 h h h为偶数,那么就可以剪切成 h / 2 × w h/2 \times w h/2×w的明信片,同理如果 w w w为偶数也可行。问你是否可以剪切出至少 n n n张卡片。 -
解题思路
将 h h h或 w w w减小则数量 × 2 \times 2 ×2,判断能分多少次即可。 -
AC代码
/**
*@filename:A
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-06-10 08:25
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;
int t,h,w,n;
void solve(){
int cnt = 1;
while(w % 2 == 0){
cnt *= 2;
w /= 2;
}
while(h % 2 == 0){
cnt *= 2;
h /= 2;
}
//cout << cnt << endl;
if(cnt >= n){
puts("YES");
}
else{
puts("NO");
}
}
int main(){
cin >> t;
while(t -- ){
cin >> w >> h >> n;
solve();
}
return 0;
}
B. Fair Division
-
题意
给你 n n n个糖果,需要你判断能否公平分配使得两个人的糖果重量相同。 -
解题思路
我们能肯定的是两个人分到的糖果重量一定是糖果总量的 1 / 2 1/2 1/2,所以糖果总量一定是偶数。然后我们考虑能不能分出这个糖果总量的 1 / 2 1/2 1/2即可,即通过sort从大开始往前减判断是否可以为 0 0 0。 -
AC代码
/**
*@filename:B
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-06-10 08:29
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;
int t,n,a[110];
int sum;
void solve(){
if(sum & 1){
puts("NO");
}
else{
sum /= 2;
sort(a + 1,a + 1 + n);
for(int i = n; i >= 1; -- i){
if(sum < a[i])break;
sum -= a[i];
}
if(sum > 0 && sum != a[1]){
puts("NO");
}
else{
puts("YES");
}
}
}
int main(){
cin >> t;
while(t -- ){
cin >> n;
sum = 0;
for(int i = 1;i <= n; ++ i){
cin >> a[i];
sum += a[i];
}
solve();
}
return 0;
}
C. Long Jumps
-
题意
Polycarp在玩一个游戏,给定有n个元素的数组a,设定一个起始点 i i i。当 i ≤ n i \leq n i≤n 时, i = i + a [ i ] i = i + a[i] i=i+a[i],得分 s c o r e = s c o r e + a [ i ] score = score + a[i] score=score+a[i],试求出对于数组a可以得出的最大得分。 -
解题思路
简单的动态规划。我们用 f [ i ] f[i] f[i]来表示到达了 i i i位置,且选取了 a [ i ] a[i] a[i]能获得的最大得分。那么状态转移方程易得为: f [ i ] = f [ i + a [ i ] ] + f [ i ] f[i] = f[i +a[i]] + f[i] f[i]=f[i+a[i]]+f[i]。这里要注意我们需要倒推得到,因为如果往前推,那么前面的 f [ i ] f[i] f[i]会被更新,而我们恰好需要使用前面的 f [ i ] f[i] f[i]。 -
AC代码
/**
*@filename:C
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-06-10 08:40
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200000 + 5;
const int P = 1e9+7;
int t,n;
ll f[N];//表示到达了i位置,且选取了a[i]所能获得的最大得分。
//则f[i] = f[i + a[i]] + f[i];
void solve(){
ll maxx = 0;
for(int i = n; i >= 1; -- i){
if(i + f[i] <= n){
f[i] += f[i + f[i]];
}
maxx = max(f[i],maxx);
}
printf("%lld\n",maxx);
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &f[i]);
}
solve();
}
return 0;
}
D. Even-Odd Game
-
题意
Alice和Bob玩游戏,其中Alice先行动。每回合玩家选择任何元素删除,如果Alice选择偶数,那么会将该值加分,如果选择奇数则分数不变。Bob反之。问游戏结束后谁的分最高。 -
解题思路
Alice选择even会获得分数,而选择odd不会获得分数,同样,Bob选择odd会获得分数,选择even不会获得分数。每个人的策略都是选择最大的提高在自己的分数,破坏对手的分数。所以我们将分数从大到小排序,每次选择最大的数字,若能让自己加分,就加,否则就拿走。 -
AC代码
/**
*@filename:D
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-06-10 08:46
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200000 + 5;
const int P = 1e9+7;
int t,n,a[N];
void solve(){
sort(a + 1,a + 1 + n,greater<int>() );
ll cnt1 = 0,cnt2 = 0;
for(int i = 1; i <= n; ++ i){
if((i & 1) && (a[i] % 2 == 0)){
cnt1 += a[i];
}
else if((i % 2 == 0) && (a[i] & 1)){
cnt2 += a[i];
}
}
if(cnt1 > cnt2){
puts("Alice");
}
else if(cnt1 == cnt2){
puts("Tie");
}
else{
puts("Bob");
}
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
solve();
}
return 0;
}
E. Correct Placement
-
题意
第 i i i个朋友占据一个矩形为 h i ∗ w i h_i * w_i hi∗wi或者 w i ∗ h i w_i * h_i wi∗hi。如果第 j j j个朋友的矩形比第 i i i个朋友的矩形小,则可以放在第i个朋友前面。即满足: h j < h i a n d w j < w i h_j < h_i \ and \ w_j < w_i hj<hi and wj<wi 或者 h j < w i a n d w j < h i h_j < w_i \ and \ w_j < h_i hj<wi and wj<hi。问每个朋友可以放置在其前面的朋友编号。 -
解题思路
sort按高排序即可,需要注意的一点就是当高度相同的时候,我们需要将宽从大到小排,因为高度相同本来就是不行的,所以为了方便处理的时候,我们可以视没有比它高度小的处理。
同时,由于可以翻转,所以我们要保存每个人的两种矩形,需要注意保存编号。
注意细节处理即可。 -
AC代码
/**
*@filename:E
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-06-10 09:00
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 400000 + 5;
const int P = 1e9+7;
const int INF = 0x3f3f3f3f;
int t,n;
struct node{
int h, w, id;
bool operator < (const node &A){
//由于等于的明显不行了,所以我们为了区分,设置w > a.w利于区分。相同级的。
return h == A.h ? w > A.w : h < A.h;
}
}a[N];
int tot,ans[N];
void solve(){
sort(a + 1,a + 1 + tot);
memset(ans, -1, sizeof(ans));
int minn = INF, pos;//保存前面最小的w。由于h一定是最小的了。
for(int i = 1; i <= tot; ++ i){
if(a[i].w > minn){
ans[a[i].id] = pos;
}
else{
pos = a[i].id, minn = a[i].w;
}
}
for(int i = 1; i <= n; ++ i){
printf("%d ", ans[i]);
}
puts("");
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
int h,w;
tot = 0;
for(int i = 1; i <= n; ++ i){
scanf("%d%d", &h, &w);
a[++ tot].h = h, a[tot].w = w, a[tot].id = i;
a[++ tot].h = w, a[tot].w = h, a[tot].id = i;
}
solve();
}
return 0;
}
F. New Year’s Puzzle
-
题意
有 2 ∗ n 2 * n 2∗n的网格条,有m个单元格被阻塞了,需要你判断是否可以将 2 ∗ 1 2 * 1 2∗1的瓷砖填补所有空格。 -
解题思路
我们这样思考,每一列无非就是四种情况,即 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3,而特殊的是,我们。而我们考虑的实际上也是列与列之间的关系。我们需要保存前一列的情况,然后跟现在判断的列进行讨论即可:
若当前列为 3 3 3,即已经填满,所以前一列必须为 3 3 3;若当前列为 2 2 2,则说明第二行是堵塞的,即我们需要填充第一行,这个时候我们可以假定后一行能填充,那么下一行的状态就是 a + 1 , 1 a + 1,1 a+1,1了。这个时候通过这个状态去判断即可,同理对于 1 1 1也是。
所以在特判 1 , 2 1,2 1,2的时候我们需要注意中间的空白行是否符合条件。 -
AC代码
/**
*@filename:F
*@author: pursuit
*@created: 2021-09-01 17:56
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, m;
map<int, int> p;
void solve(){
p[n + 1] = 3;
int x = 0, y = 3;//x代表前一列,y代表前一列的状态。
for(auto iter : p){
int a = iter.first, b = iter.second;
if(b == 3){
//由于该行为3已填满,判断前一行是否填满。
if(y != 3){
puts("NO");
return;
}
x = a;
}
else if(b == 1){
//由于b = 1,所以如果前一列已经填满,则无法通过前一列,所以这里需要通过后一列,则
//后移一位判断。
if(y == 3){
x = a + 1, y = 2;
}
else if((y == 1 && (x - a) % 2 == 0) || (y == 2 && (x - a) % 2 != 0)){
puts("NO");
return;
}
else{
x = a, y = 3;
}
}
else if(b == 2){
if(y == 3){
x = a + 1, y = 1;
}
else if((y == 2 && (x - a) % 2 == 0) || (y == 1 && (x - a) % 2 != 0)){
puts("NO");
return;
}
else{
x = a, y = 3;
}
}
}
puts("YES");
}
int main(){
scanf("%d", &t);
while(t -- ){
p.clear();
scanf("%d%d", &n, &m);
int r, c;
for(int i = 1; i <= m; ++ i){
scanf("%d%d", &r, &c);
p[c] += r;
}
solve();
}
return 0;
}
G. Moving to the Capital
- 题意
n个城市,编号为1的是首都。道路单向且长度为1.每个城市都有一个值 d i d_i di,为从首都1到第i个城市的最短距离。在编号为 i i i的城市开始,在第 i i i个城市选择:
- 如果有路 ( i , j ) (i,j) (i,j),且 d i < d j d_i < d_j di<dj,那么就从 i − > j i -> j i−>j;
- 如果有路 ( i , j ) (i,j) (i,j),且 d i ≥ d j d_i\geq d_j di≥dj,那么就从 i − > j i -> j i−>j;
- 停止旅行。
第二种走法最多执行一次,问对于每个点 i i i,走若干次后到达的点距离 1 1 1最小是多少。
- 解题思路
首先我们需要求出首都到每个点的最短距离是多少,因为这是我们的参考依据,这个过程可以通过 b f s bfs bfs实现。处理完之后,我们就可以开始 d f s dfs dfs搜索判定了,由于对每个点这样进行操作过于复杂, 所以我们可以实现记忆化搜索,我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示从 i i i出发,且当前状态为 j j j的距离首都最近的距离。其中 j j j表示是否使用了第二种走法,那么状态转移自然是根据 d i d_i di和 d j d_j dj决定的,据此判定即可。 - AC代码
/**
*@filename:G
*@author: pursuit
*@created: 2021-09-01 15:52
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, m, u, v;
struct edge{
int to, next;
}edges[N];
int head[N], tot;
int dist[N], dp[N][2];
void add(int u, int v){
edges[++ tot].to = v;
edges[tot].next = head[u];
head[u] = tot;
}
void bfs(){
dist[1] = 0;
queue<int> q;
q.push(1);
while(!q.empty()){
u = q.front();
q.pop();
for(int i = head[u]; i; i = edges[i].next){
v = edges[i].to;
if(dist[v] == INF){
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
}
int dfs(int u, int flag){
//flag为1表示还有一次机会可以选择第二种操作。否则不可选择。
if(dp[u][flag] != -1)return dp[u][flag];
dp[u][flag] = dist[u];
for(int i = head[u]; i; i = edges[i].next){
int v = edges[i].to;
if(dist[u] < dist[v]){
dp[u][flag] = min(dp[u][flag], dfs(v,flag));
}
else if(flag){
dp[u][flag] = min(dp[u][flag], dfs(v, 0));
}
}
return dp[u][flag];
}
void solve(){
bfs();
//开始dp。
for(int i = 1; i <= n; ++ i){
printf("%d ", min(dfs(i, 0), dfs(i, 1)));
}
puts("");
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d%d", &n, &m);
memset(head, 0, sizeof(head));
memset(dp, -1, sizeof(dp));
fill(dist, dist + N, INF);
tot = 0;
for(int i = 1; i <= m; ++ i){
scanf("%d%d", &u, &v);
add(u, v);
}
solve();
}
return 0;
}

浙公网安备 33010602011771号