Codeforces Round #743 (Div. 2) A—C

A. Countdown

题意:

      有一个有前导零的数字,每次操作可以使该数字减一,或者交换该数字上任意两个位置的数。
      询问最少需要多少次操作可以使得该数变为全0。

分析:

      可以发现对于位数越低的位,减一操作越少,所以我们可以贪心的进行减一操作,即将其他所有位置上的数字都交换到个位上进行减一,所以答案就是所有位上的数字加和,加上除最后一位以外非0位的个数(交换次数)。

ACcode:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

typedef long long ll;
const int N = 3e5 + 100;
const int mod = 998244353;

void solve() {
    int n;
    ll res = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i) {
        int x;
        scanf("%1d", &x);
        if(x != 0 && i != n) res ++;
        res += x;
    }
    printf("%lld\n", res);  
}

int main() {
    int t;
    scanf("%d", &t);
    while(t --)
        solve();
    return 0;
}

/*

*/
 

B. Swaps

题意:

      有一个包含从 [ 1 , 2 n ] [1,2n] [1,2n]内的所有奇数的无序数组A,还有一个包含从 [ 1 , 2 n ] [1,2n] [1,2n]内的所有偶数的无序数组B。
      你可以进行一种操作:首先选中两个数组中的任意一个数组,然后选择此数组两个相邻的位置,将两个位置的值交换。
      询问最少换多少次可以使得A数组的字典序小于B数组。

分析:

      首先如果想要使得A的字典序小于 B B B,那么就只需要 A 1 < B 1 A_1 < B_1 A1<B1
      我们可以找到 A A A数组中的一个数 A i A_i Ai B B B数组中的一个数 B j B_j Bj,满足 A i < B j A_i < B_j Ai<Bj,然后将 A i A_i Ai B j B_j Bj同时通过操作移动到数组的第一位,那么答案就是 i + j − 2 i + j - 2 i+j2
      因为 A A A B B B数组里的数都是固定的,所以我们可以通过 O ( 2 ∗ n ) O(2*n) O(2n)的复杂度枚举每一个数字,处理出来 大于当前奇数的所有的偶数 移动到 B B B数组的第一位所需要的操作数的最小值 x x x,设 y y y为当前奇数移动到 A A A数组的第一位所需要的操作数, x + y x+y x+y即为将当前奇数移动到 A A A数组的第一位的所有情况中的最小值。
      再将 [ 1 , 2 n ] [1,2n] [1,2n]所有的奇数都做此操作,对所有的 x + y x+y x+y求一个最小值,即为答案。

ACcode:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

typedef long long ll;
const int N = 3e5 + 100;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;

int mp[N]; //mp[i]表示将i这个数字移动到数组首位需要的操作数

void solve() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; ++ i) {
        int x;
        scanf("%d", &x);
        mp[x] = i;
    }

    for(int i = 0; i < n; ++ i) {
        int x;
        scanf("%d", &x);
        mp[x] = i;
    }

    int ans = inf, res = inf;
    for(int i = 2 * n; i >= 1; -- i) {
        if(i & 1) ans = min(ans, mp[i] + res);
        else res = min(res, mp[i]);
    }
    printf("%d\n", ans);
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("1.in", "r", stdin);
        freopen("1.out", "w", stdout);
    #endif
    int t;
    scanf("%d", &t);
    while(t --)
        solve();
    return 0;
}

/*
5
9 3 7 1 5
2 8 6 4 10
*/
 

C. Book

题意:

      有一本书,里面有n个章节。为了去理解某些章节,需要先把此章节的所有前置章节理解了。
      刚开始的时候你不理解任何一个章节,你会从头到尾阅读这本书,遇到可以理解的章节就会阅读并理解,求需要阅读这本书多少次可以将全部章节理解。

分析:

      很明显就是一个拓扑排序问题,这里有两种做法:

第一种:

      使用优先队列进行拓扑排序,保证先处理轮数小的章节。
      对于一个入度为0的点y来说,如果将y到x的边删除之后,使得x的入度为0,那么就比较x和y编号的大小关系,如果y < x的话,说明x这个章节也可以在这一轮内被理解;如果x < y的话,说明x这个章节需要在下一轮中才能被理解。
      最终读书的轮数+1就是答案,如果原图不是个拓扑图答则案为-1。

代码:
#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 100;
typedef pair<int, int> PII;

int n;
int d[N];

void solve() {
    priority_queue<PII, vector<PII>, greater<PII> > Q;
    scanf("%d", &n);
    vector<int> G[n + 1];
    for(int i = 1; i <= n; ++ i) d[i] = 0;
    for(int i = 1; i <= n; ++ i) {
        int k;
        scanf("%d", &k);
        while(k --) {
            int p;
            scanf("%d", &p);
            G[p].push_back(i);
            d[i] ++;
        }
    }

    int res = 0, ans;
    for(int i = 1; i <= n; ++ i) if(!d[i]) Q.push(make_pair(1, i));

    while(Q.size()) {
        auto head = Q.top();
        Q.pop();
        res ++, ans = head.first;
        for(int j = 0; j < G[head.second].size(); ++ j) {
            int to = G[head.second][j];
            d[to] --;
            if(!d[to]) {
                if(to < head.second) Q.push(make_pair(head.first + 1, to));
                else Q.push(make_pair(head.first, to));
            }
        }
    }

    if(res == n) printf("%d\n", ans);
    else printf("-1\n");
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("1.in", "r", stdin);
        freopen("1.out", "w", stdout);
    #endif
    int t;
    scanf("%d", &t);
    while(t --) {
        solve();
    }
    return 0;
}
第二种:

      直接建图,若a→b,且a < b的话,则边权为0,否则边权为1(边权为0代表能在当前轮理解,边权为1表示需要在下一轮理解)。
      我们只需要对这个图求一个最长路径,这个最长路径+1就是答案(最长路径表示需要经过这么多轮才能到达最后的那个点)。
      最长路按拓扑序dp一下就可以求出来。

代码:
#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 100;
int h[N], ne[N], e[N], w[N], idx;
int dp[N], d[N];
vector<int> sorted;
int n;

void init() {
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n; ++ i) dp[i] = d[i] = 0;
    idx = 0;
}

void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

bool topsort() {
    queue<int> Q;
    sorted.clear();
    for(int i = 1; i <= n; ++ i) if(!d[i]) Q.push(i);
    while(Q.size()) {
        int head = Q.front();
        Q.pop();
        sorted.push_back(head);
        
        for(int i = h[head]; ~i; i = ne[i]) {
            int to = e[i];
            d[to] --;
            if(!d[to]) Q.push(to);
        }
    }
    return sorted.size() == n;
}

void solve() {
    scanf("%d", &n);
    init();
    for(int i = 1; i <= n; ++ i) {
        int k;
        scanf("%d", &k);
        while(k --) {
            int p;
            scanf("%d", &p);
            add(p, i, (p < i ? 0 : 1));
            d[i] ++;
        }
    }

    int res = topsort();
    if(!res) {
        printf("-1\n");
        return ;
    }

    for(int i = 0; i < sorted.size(); ++ i) {
        int v = sorted[i];
        for(int j = h[v]; ~j; j = ne[j]) {
            int to = e[j];
            if(dp[to] < dp[v] + w[j])
                dp[to] = dp[v] + w[j];
        }
    }

    int ans = 0;
    for(int i = 1; i <= n; ++ i) ans = max(ans, dp[i]);
    printf("%d\n", ans + 1);

}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("1.in", "r", stdin);
        freopen("1.out", "w", stdout);
    #endif
    int t;
    scanf("%d", &t);
    while(t --) {
        solve();
    }
    return 0;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值