第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(南京)E.Evil Coordinate (分类讨论+模拟)

12 篇文章 0 订阅
这篇博客讨论了一种算法问题,即如何让机器人从起点(0,0)到达终点(x,y),同时避免途经一个位于(m,n)的炸弹。文章详细分析了四种不可能的情况,并针对不同终点位置提出了不同的解决方案,包括先横后竖和先竖后横的路径规划。博主通过枚举UDLR全排列的方法找到了简洁的解答,并提供了具体的C++代码实现。
摘要由CSDN通过智能技术生成

https://ac.nowcoder.com/acm/contest/21739/E

  • 这题有更简单的做法(枚举UDLR全排列),我用这个分类讨论的方法折腾了2个半小时才终于AC…

思路:

  • 1.首先我们可以想到,给定了一个走法的序列,不管这个序列的排列是怎样,它的终点一定确定了.所以炸弹(mx,my)如果在起点(0,0)或终点(x,y)那结果肯定是 I m p o s s i b l e Impossible Impossible.

  • 2.还有一种 I m p o s s i b l e Impossible Impossible的情况就是当X轴方向没有步数( C n t R = 0 & & C n t L = 0 Cnt_R=0\&\& Cnt_L=0 CntR=0&&CntL=0)且炸弹在Y轴方向的必经之路上;当Y轴方向没有步数( C n t U = 0 & & C n t D = 0 Cnt_U=0\&\& Cnt_D=0 CntU=0&&CntD=0)且炸弹在X轴方向的必经之路上.

  • 3.除去上面两种,剩余情况都是总能找到一个序列使机器人不经过炸弹的. 我们可以分情况讨论:

    • a.终点不在坐标轴上

      我们将路径简化为两种,先横着走再后竖着走和先竖着走再横着走.

      image-20211013230143511

    这种情况比较简单,只需要判断炸弹是否在任意一条路径上,那么不走这条路径即可.例如:若炸弹在1上,那就走2;若炸弹在2上或者炸弹在其他地方,那就走1.

    • b.终点在坐标轴上

      这种情况看似简单,实则比较复杂.例如:终点在Y轴上,那说明L和R的步数相等.但先L或先R可能会对答案造成影响,所以我们要进行一些判断.例如:炸弹若在X轴下方,那么我们先U后D肯定不会有问题;炸弹若在Y轴左方,那先R和L肯定不会有问题.(会有问题的我们已经在第二种impossible中筛掉了).这里语言描述起来会比较麻烦,直接看代码应该会比较好理解.

  • 4.最后要注意一下正负号的问题,然后把每种情况交代清楚,应该就能AC了.

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<unordered_map>
#include<set>
//#pragma GCC optimize(2)
//#pragma GCC optimize("inline")
//#pragma GCC optimize("-fgcse")
//#pragma GCC target("avx","sse2")
//#pragma GCC optimize("-fgcse-lm")
//#pragma GCC optimize("-fipa-sra")
//#pragma GCC optimize("-ftree-pre")
//#pragma GCC optimize("-ftree-vrp")
//#pragma GCC optimize("-fpeephole2")
//#pragma GCC optimize("-ffast-math")
//#pragma GCC optimize("-fsched-spec")
//#pragma GCC optimize("unroll-loops")
using namespace std;
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define PIII pair<int,PII>
#define PLLL pair<ll,PLL>
#define fi first
#define se second
#define pb push_back
#define debug(a) cout << #a << " " << a << '\n';
const int N = 1e5 + 5;
const int M = 1e5 + 5;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;

ll mx, my;

bool tong(ll x, ll y) {//判断两个数是否同号
    if (x == 0 || y == 0)return true;
    if (x >= 0 && y >= 0)return true;
    if (x <= 0 && y <= 0)return true;
    return false;

}

void solve() {
    map<char, ll> mp;
    cin >> mx >> my;
    string path;
    cin >> path;
    int len = path.length();
    for (int i = 0; i < len; i++) {
        if (path[i] == 'L')mp['L']++;
        else if (path[i] == 'R')mp['R']++;
        else if (path[i] == 'U')mp['U']++;
        else if (path[i] == 'D')mp['D']++;
    }
    ll x = mp['R'] - mp['L'];//计算终点(一定会到达)
    ll y = mp['U'] - mp['D'];
    //炸弹的位置在终点或起点
    if ((mx == x && my == y) || (mx == 0 && my == 0)) {
        cout << "Impossible\n";
    }
        //只能在Y轴移动,炸弹在必经之路上
    else if ((!mp['L'] && !mp['R']) && (tong(my, y) && abs(my) <= abs(y) && mx == 0)) {
        cout << "Impossible\n";
    }
        //只能在X轴移动,炸弹在必经之路上
    else if ((!mp['U'] && !mp['D']) && (tong(mx, x) && abs(mx) <= abs(x) && my == 0)) {
        cout << "Impossible\n";
    }
    else {
        //L和R最终抵消了
        if (x == 0) {
            string ans;
            if (mx > 0) {//炸弹在Y轴右侧,那么就先走左面
                for (int i = 0; i < mp['L']; i++) ans += 'L';
                if (my < 0) {
                    //炸弹在X轴下侧,那么就先走上面
                    for (int i = 0; i < mp['U']; i++) ans += 'U';
                    for (int i = 0; i < mp['D']; i++) ans += 'D';
                }
                else {
                    for (int i = 0; i < mp['D']; i++) ans += 'D';
                    for (int i = 0; i < mp['U']; i++) ans += 'U';
                }
                for (int i = 0; i < mp['R']; i++) ans += 'R';
            }
            else {
                for (int i = 0; i < mp['R']; i++) ans += 'R';
                if (my < 0) {
                    for (int i = 0; i < mp['U']; i++) ans += 'U';
                    for (int i = 0; i < mp['D']; i++) ans += 'D';
                }
                else {
                    for (int i = 0; i < mp['D']; i++) ans += 'D';
                    for (int i = 0; i < mp['U']; i++) ans += 'U';
                }
                for (int i = 0; i < mp['L']; i++) ans += 'L';
            }
            cout << ans << '\n';
            return;
        }
        //U和D最终抵消了
        if (y == 0) {
            string ans;
            if (my > 0) {
                //炸弹在X轴上侧,那么就先走下面
                for (int i = 0; i < mp['D']; i++) ans += 'D';
                if (mx < 0) {//炸弹在Y轴左侧,那么就先走右面
                    for (int i = 0; i < mp['R']; i++) ans += 'R';
                    for (int i = 0; i < mp['L']; i++) ans += 'L';
                }
                else {
                    for (int i = 0; i < mp['L']; i++) ans += 'L';
                    for (int i = 0; i < mp['R']; i++) ans += 'R';
                }
                for (int i = 0; i < mp['U']; i++) ans += 'U';
            }
            else {
                for (int i = 0; i < mp['U']; i++) ans += 'U';
                if (mx < 0) {
                    for (int i = 0; i < mp['R']; i++) ans += 'R';
                    for (int i = 0; i < mp['L']; i++) ans += 'L';
                }
                else {
                    for (int i = 0; i < mp['L']; i++) ans += 'L';
                    for (int i = 0; i < mp['R']; i++) ans += 'R';
                }
                for (int i = 0; i < mp['D']; i++) ans += 'D';
            }
            cout << ans << '\n';
            return;
        }
        //终点不在坐标轴上,可以通过先横后竖或者先竖后横的方法走
        if ((my == 0 || mx == x)) {//如果炸弹不在先横后竖的走法上,那就走先竖后横
            string ans;
            for (int i = 0; i < mp['U']; i++) ans += 'U';
            for (int i = 0; i < mp['D']; i++) ans += 'D';
            for (int i = 0; i < mp['L']; i++) ans += 'L';
            for (int i = 0; i < mp['R']; i++) ans += 'R';
            cout << ans << '\n';
        }
        else {
            string ans;
            for (int i = 0; i < mp['L']; i++) ans += 'L';
            for (int i = 0; i < mp['R']; i++) ans += 'R';
            for (int i = 0; i < mp['U']; i++) ans += 'U';
            for (int i = 0; i < mp['D']; i++) ans += 'D';
            cout << ans << '\n';
        }
    }

}

int main() {
    ios::sync_with_stdio(false);
#ifdef LOCAL
    int begin_time = clock();
    freopen("../input.txt", "r", stdin);
//    freopen("../output.txt", "w", stdout);
#endif
    int T = 1;
    cin >> T;
    for (int cas = 1; cas <= T; cas++) {
#ifdef LOCAL
        printf("Case #%d: ", cas);
#endif
        solve();
    }

#ifdef LOCAL
    int end_time = clock();
    printf("\nRun time: %.2lf ms", (double) (end_time - begin_time) / CLOCKS_PER_SEC * 1000);
#endif

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值