2019 ICPC Malaysia National 题解

博客包含多道算法题,如方形图案旋转、时间选取、士兵配对等。针对不同题目给出题意和思路,像图案旋转模拟操作,时间选取用01背包,士兵配对用状态压缩,大小关系排序用拓扑排序等,部分题目还给出盲猜思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Mental Rotation

题意:给你一个方形图案,给一系列右旋转或者左旋转操作,要求输出操作后的图案
思路:因为最多只有右旋90,180,270度这三种,我们处理完所有的操作后模拟旋转就行。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn = 1005;
char s[maxn][maxn], str[maxn][maxn];
char mv[maxn], lj[40] = {">v<^"};
int n;
void gao(int i, int j, int &x, int &y, int res) {
    if (res == 1)
        x = j, y = n + 1 -i;
    if (res == 2)
        x = n + 1 - i, y = n + 1 - j;
}
int main() {
    scanf("%d%s", &n, mv + 1);
    for (int i = 1; i <= n; i++)
        scanf("%s", s[i] + 1);
    int res = 0;
    for (int i = 1; mv[i]; i++)
        if (mv[i] == 'L')
            res--;
        else
            res++;
    while (res < 0)
        res += 4;
    while (res > 4)
        res -= 4;
    if (res == 0 || res == 4) {
        for (int i = 1; i <= n; i++)
            printf("%s\n", s[i] + 1);
        return 0;
    }
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) {
        int x, y;
        if (res == 3) {
            gao(i, j, x, y, 1);
            gao(x, y, x, y, 2);
        }
        else
            gao(i, j, x, y, res);
        int p = 0;
        char c = '.';
        if (s[i][j] == c) {
            str[x][y] = '.';
            continue;
        }
        for (;; p++)
            if (lj[p] == s[i][j])
                break;
        p += res;
        p %= 4;
        c = lj[p];
        str[x][y] = c;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++)
            printf("%c", str[i][j]);
        puts("");
    }
}

B. SpongeBob SquarePants
签到
C. I Don’t Want To Pay For The Late Jar!
签到
D. Ali The Multi-billionaire
待补
E. Optimal Slots

题意:给一个总时间T,再给N个时间,要求选取一些时间ti,使得总和sum不超过T且尽量接近T,如果有多种方案,就输出输入顺序早的方案。
思路:我们设 d[i][j] 为能否从 i 到 N 这些时间段选取一些组成 j ,为1可行,为0不可行,转移方程非常简单,就是个01背包:从大到小枚举 j,d[i][j] |= d[i + 1][j - t[i]],我们找到一个最大的 j,使得d[1][j] = 1,那么答案 sum 就是 j,然后我们依次从小到大枚举 i,如果 d[i + 1][sum - t[i]] = 1,那么我们就选取 ti,并且sum -= ti,如果不等于1,跳过ti继续枚举。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 24 * 8;
int d[52][maxn], a[51];
int main() {
    int T, n;
    for (int i = 1; i <= 51; i++)
        d[i][0] = 1;
    while (cin>>T && T) {
        cin>>n;
        for (int i = 1; i<= n; i++) {
            cin>>a[i];
            for (int j = 1; j <= T; j++)
            d[i][j] = 0;
        }
        for (int j = 1; j <= T; j++)
            d[n + 1][j] = 0;
        for (int i = n; i; i--) {
            for (int j = T; j >= a[i]; j--)
                d[i][j] |= d[i + 1][j - a[i]];
            for (int j = T; j; j--)
                d[i][j] |= d[i + 1][j];
        }
        int sum = T;
        while (!d[1][sum])
            sum--;
        int ans = sum;
        int p = 1;
        queue<int> q;
        while (sum > 0) {
            if (a[p] <= sum) {
                if (d[p + 1][sum - a[p]])
                    q.push(a[p]), sum -= a[p];
            }
            p++;
        }
        while (!q.empty()) {
            printf("%d ", q.front());
            q.pop();
        }
        printf("%d\n", ans);
    }
}

F. Military Class

题意:有2*N个士兵站成两排,现在要求上面一排的士兵和下面一排的士兵–配对,要求配对的两个士兵,他们的位置相差不能超过e,而且有K队士兵互相讨厌,不能给他们配对,要求输出配对总方案数。
思路:考虑到 e 最大为4,我们状态压缩记录下面哪些士兵已经匹配了,设d[i][j]为上面匹配到第 i 个士兵,下面 i - e 到 i + e个士兵中未被匹配的集合为 j 的方案数,假设第 i 个士兵的状态为11010,e为2,那么表示下面第 i 个士兵和第 i + 2 士兵未被匹配,然后我们枚举未被匹配的士兵 k,如果上面第 i + 1 个士兵不讨厌士兵 k ,假设 k 为 i,那么转移方程:d[i + 1][11100] += d[i][11010],或许你好奇,i + 1 的状态为啥不是 11110,其实到了第 i + 1 个人,他记录的状态是从 i - e + 1开始的,同理,如果 k 为 i + 2,转移方程:d[i + 1][10110] += d[i][11010],此为第 i +1 士兵还可以选择下面 i +1 + e个士兵,如果他们不互相讨厌:d[i + 1][10101] += d[i][11010]。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2010, mod = 1e9 + 7;
ll d[maxn][1<<11];
int ban[maxn][maxn];
void add(ll &x, ll y) {
    x += y;
    if (x >= mod)
        x -= mod;
}
int main() {
    int n, e, k, u, v;
    cin>>n>>e>>k;
    for (int i = 1; i <= k; i++) {
        cin>>u>>v;
        ban[u][v] = 1;
    }
    int m = 2 * e + 1;
    int s = 1 << m;
    d[0][0] = 1;
    for (int i = 0; i < n; i++)
    for (int j = 0; j < s; j++) {
        if (!d[i][j])
            continue;
        int k1 = max(1, i - e);
        int k2 = min(n, i + e + 1);
        int s0 = (j << 1);
        s0 &= (s - 1);
        for (int k = k1; k <= k2; k++) {
            int p = m - 1 - (k - (i - e));
            if (p == -1)
                if (!ban[i + 1][k]) {
                    add(d[i + 1][s0 | 1], d[i][j]);
                continue;
            }
            if (ban[i + 1][k] || (j & (1 << p)) || p == m - 1)
                continue;
            add(d[i + 1][s0 | (1 << p + 1)], d[i][j]);
        }

    }
    ll ans = 0;
    for (int i = 1; i < s; i++)
        add(ans, d[n][i]);
    printf("%lld\n", ans);
}

H. Are You Safe?
题解:轩少

I. To Crash Or Not To Crash
题意:问起点往右走是否能安全通过,如果不能,就输出第一个撞到的障碍物。题目不难,题意难。
J. Kitchen Plates

题意:给5个大小关系,要求按照他们的关系从小到大依次输出。
思路:只有5个可以暴力做,提供一种拓扑排序思路,对于 a < b,那么 b 的度数 ++,并且从 a 到 b 引入一条有向边,最后我们把所有度数为0的点加入队列,枚举他们,假设当前枚举的是 u,枚举 u 连接的点 v,把 v 的度数–,如果v的度数为0,把v加入队列,最后如果还有点度数不为0,那么无解,否则就按照出队顺序输出5个点就行。
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
int  d[5];
vector<int> G[5];
int id(char c) {
    return c - 'A';
}
int main() {
    char c1, c2, c;
    for (int i = 1; i <= 5; i++) {
        cin>>c1>>c>>c2;
        int u = id(c1);
        int v = id(c2);
        if (c == '>') {
            d[u]++;
            G[v].pb(u);
        }
        else {
            d[v]++;
            G[u].pb(v);
        }
    }
    queue<char>q, q2;
    for (int i = 0; i < 5; i++)
        if (!d[i])
            q.push(i + 'A');

    while (!q.empty()) {
        int u = q.front() - 'A';
        q2.push(u + 'A');
        q.pop();
        for (auto v : G[u]) {
            d[v]--;
            if (!d[v])
                q.push(v + 'A');
        }
    }
    if (q2.size() != 5)
        puts("impossible");
    else {
        while (!q2.empty())
            printf("%c", q2.front()), q2.pop();
    }
}

K. Help The Support Lady

题意:没读懂,但是盲猜题意过了,盲猜:把所有 ti 从小到大排序,开始枚举ti并且记录前缀和sum[i],如果sum[i - 1] <= t[i],那么答案++,并且sum[i] = sum[i - 1] + t[i],否则sum[i] = sum[i - 1]
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        ll sum = 0, ans = 0;
        for (int i = 1; i <= n; i++) {
            if (sum <= a[i]) {
                sum += a[i];
                ans++;
            }
        }
        printf("Case #%d: %lld\n", ++kase, ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值