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 in 时, 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 hiwi或者 w i ∗ h i w_i * h_i wihi。如果第 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 2n的网格条,有m个单元格被阻塞了,需要你判断是否可以将 2 ∗ 1 2 * 1 21的瓷砖填补所有空格。

  • 解题思路
    我们这样思考,每一列无非就是四种情况,即 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个城市选择:
  1. 如果有路 ( i , j ) (i,j) (i,j),且 d i < d j d_i < d_j di<dj,那么就从 i − > j i -> j i>j
  2. 如果有路 ( i , j ) (i,j) (i,j),且 d i ≥ d j d_i\geq d_j didj,那么就从 i − > j i -> j i>j
  3. 停止旅行。
    第二种走法最多执行一次,问对于每个点 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;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(46)  评论(0)    收藏  举报