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 ≤
0 ≤ A, B, C ≤
si is AB
, AC
, 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.贪心的做法,有一些细节处理,比如怎么存,怎么输出,字符转换后可以用数组表示等等。