AtCoder - ABC 166 - F(DFS / 贪心)

F - Three Variables Game

题意:

给定字母A,B,C各自的值,共有三种操作:

  • AB: AB 其中一个字母的值加1,另一个字母的值减1
  • BC:BC 其中一个字母的值加1,另一个字母的值减1
  • AC:AC 其中一个字母的值加1,另一个字母的值减1

共操作 N 次,每次输入的是字符串。问能否在保证三个数都非负的条件下进行 n 次操作,如果可以输出Yes以及每次操作进行加1的那个字母;如果不能输出No。

数据范围:

1 ≤ N ≤ 10^{5}

0 ≤ A, B, C ≤ 10^{9}

si​ is ABAC, or BC

思路1(DFS):

直接爆搜DFS枚举出所有情况,如果存在总能找到。

Code:

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

#define x first
#define y second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };

const int N = 100010, INF = 0x3f3f3f3f, mod = 1e9 + 7;

typedef pair<int, int>PII;

int n, a, b, c;
string s[N];
char ans[N];

void dfs(int x, int a, int b, int c)
{
    if (x > n)                                        // x>n说明此时已经进行了n个操作
    {
        puts("Yes");
        for (int i = 1; i <= n; i++)cout << ans[i] << endl;   // 输出每次操作对应执行+1的字母
        exit(0);                                     // exit(0)是系统结束,即输出完毕
    }
    if (s[x] == "AB")
    {
        if (a) ans[x] = 'B', dfs(x + 1, a - 1, b + 1, c);
        if (b) ans[x] = 'A', dfs(x + 1, a + 1, b - 1, c);
    }
    else if (s[x] == "AC")
    {
        if (a) ans[x] = 'C', dfs(x + 1, a - 1, b, c + 1);
        if (c) ans[x] = 'A', dfs(x + 1, a + 1, b, c - 1);
    }
    else
    {
        if (b) ans[x] = 'C', dfs(x + 1, a, b - 1, c + 1);
        if (c) ans[x] = 'B', dfs(x + 1, a, b + 1, c - 1);
    }
}

void solve()
{
    cin >> n >> a >> b >> c;
    for (int i = 1; i <= n; i++) cin >> s[i];     // 用二维字符串数组装所输入n个字符串
    dfs(1, a, b, c);                           // dfs枚举所有情况,第一个参数表示第几个操作
    puts("No");                                // 在dfs中没有结束程序,说明进行不到第n个操作
}

signed main()
{
    //int t;
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

思路2(贪心):

参考博客

因为总共有A,B,C三个字母,每次操作两个字母。一开始的想法就是值大的字母-1后把1给值小的字母,但是这样不行,如:

输入:
3 1(A) 1(B) 0(C)
AC
AB
AC
输出:
Yes
A
C
A

如果按这种方法AC操作后为0 1 1;BC操作可操作后可能为0 2 0;这样就无法再进行再、AC操作,不成立。
正确操作应该是在BC操作中如果操作后是0 0 2;再AC操作后就是1 01成立。

所以,不能只看本次操作的数,还得考虑下一个操作的字母——
1. 如果当前两字符值不同,大值-1,小值+1;
2. 如果当前两字母值相同,考虑下一个操作用到的1。即根据下一个操作的两字母,找到当前操作中重复出现的字母(因为总共九3个字母,每次操作2个字母,第一会有重的),对该字母进行+1操作。
3. 如果需要-1的字母值<1,直接No。

实现:

1. 我们用数组 v[i] (i:0~1对应A,B,C)记录初始3个字母的值。输入 n 次操作,对于每个操作的两个字母,我们用两个数组分别存,数组 p[i] 存第 i 次操作的第一个字母,q[i] 存第二个字母()为了方便直观,存的是 字母 - 'A' 后的值,这样 p[i] 或 q[i] 的值 0 ~ 3 ,正好对应字母A,B,C,正好与数组 v 的下标对应上。也就是我们可以用 p[i] 作为 v[] 数组的下标,进而可以得对应字母的值,v[p[i]] 就表示第 i 次操作中字母p[i]对应的值)

2. 分情况讨论,因为两个字母的值+1/-1,所以我们为了方便输出,统一将进行 +1 操作的字母放在p[i] 的位置,便于操作。情况1是将小数放在 p[i] 位置;情况2是判断是否 q[i] 是重的,如果是则放在 p[i] 位置;否则不用动。

3. 位置放好后,判断需要-1的值 v[q[i]] 是否小于 1,如果是直接 No 即可。

Code:

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

#define x first
#define y second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };

const int N = 100010, INF = 0x3f3f3f3f, mod = 1e9 + 7;

typedef pair<int, int>PII;

int n;
int v[3], p[N], q[N];

void solve()
{
    cin >> n;
    for (int i = 0; i < 3; i++)cin >> v[i];                  // 输入A,B,C的初始值

    string s;
    for (int i = 1; i <= n; i++)                             // 输入n个操作
    {
        cin >> s;
        p[i] = s[0] - 'A';                                  // p[i],q[i]存的分别是第i个操作的两个字母的数字表示形式
        q[i] = s[1] - 'A';
    }

    for (int i = 1; i <= n; i++)
    {
        if (v[p[i]] > v[q[i]]) swap(p[i], q[i]);               // 如果第i次操作中第一个字母p[i]的值v[p[i]]比第二个字母大,调换一下顺序,使得值小的字母对应的是p[i]
        else if (v[p[i]] == v[q[i]])                         // 如果两字母的值相等
        {
            if (i < n && (q[i] == p[i + 1] || q[i] == q[i + 1])) // 判断下一次操作的两个字母p[i+1],q[i+1]中是否出现这次操作的字母q[i]
                swap(p[i], q[i]);                             // 因为我们是要统一对放在p[i]位置的字母操作的,所以将此时需要操作的字母q[i]放在p[i]的位置
            if (!v[q[i]]) return void(puts("No"));        // 如果出1的字母值v[q[i]]<1,说明无法操作
        }

        v[p[i]]++, v[q[i]]--;                                 //每次操作对应p[i]位置的字母值+1,q[i]位置的字母值-1
    }

    puts("Yes");
    for (int i = 1; i <= n; i++)cout << (char)('A' + p[i]) << endl;             // 根据每次操作都是p[i]位置的字母值+1,将p[i]转换为字符后输出即可
}

signed main()
{
    //int t;
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:题目注意点:

1. 这个题dfs的形式感觉相当规范,有一种规范化的美。

2.贪心的做法,有一些细节处理,比如怎么存,怎么输出,字符转换后可以用数组表示等等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值