题目
有三个杯子,容量分别为
A
,
B
,
C
A,B,C
A,B,C。
初始时,
C
C
C 杯装满了水,而
A
,
B
A,B
A,B 杯都是空的。
现在在保证不会有漏水的情况下进行若干次如下操作:
将一个杯子
x
x
x 中的水倒到另一个杯子
y
y
y 中,当
x
x
x 空了或者
y
y
y 满了时就停止(满足其中一个条件才停下)。
请问,在操作全部结束后,
C
C
C 中的水量有多少种可能性。
0 ≤ A , B , C ≤ 4000 0≤A,B,C≤4000 0≤A,B,C≤4000
思路
一开始觉得是思维题,想了一会没有思路。
一看标签是bfs/dfs 和 哈希,就想起来是像去年沈阳站那道 bfs 一样,爆搜出所有方案。
bfs, dfs 两种方式。
细节:
映射这三个数有三种方式:
- 放到结构体中,map映射结构体。但是需要在结构体中重载运算符,所有元素都要比较。
- 放到vector中,map映射vector。时间复杂度较高,前几天cf有人这样写被hack超时了,这题也超时。
- 将这三个数哈希成一个数,map映射这个数。如果三个数比较小的话,可以选择一个比较大的数作为哈希值;否则或者找一个冲突概率小的数。复杂度较优。
执行操作的方式:
一共三个数,两两之间操作,可以直接ifelse,也可以把三个数存到数组中,二重循环遍历操作。
可以直接把数组传给函数处理操作,函数中修改数组后原数组也改变。
Code
bfs,map映射结构体,ifelse处理操作:
#include<bits/stdc++.h>
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
const int N = 200010, mod = 1e9+7;
int T, n, m;
int va, vb, vc;
struct node{
int a, b, c;
friend bool operator < (node a, node b){
if(a.a!=b.a) return a.a < b.a;
else if(a.b!=b.b) return a.b < b.b;
else return a.c < b.c;
}
};
map<node, bool> f;
map<int, int> mp;
void bfs()
{
mp.clear();
f.clear();
queue<node> que;
que.push({0, 0, vc});
f[{0, 0, vc}] = 1;
while(que.size())
{
int a = que.front().a, b = que.front().b, c = que.front().c;
que.pop();
mp[c] = 1;
if(a)
{
int ta = a, tb = b, tc = c;
if(ta <= vb-tb){
tb += ta;
ta = 0;
}else{
ta -= vb-tb;
tb = vb;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
ta = a, tb = b, tc = c;
if(ta <= vc-tc){
tc += ta;
ta = 0;
}else{
ta -= vc-tc;
tc = vc;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
}
if(b)
{
int ta = a, tb = b, tc = c;
if(tb <= va-ta){
ta += tb;
tb = 0;
}else{
tb -= va-ta;
ta = va;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
ta = a, tb = b, tc = c;
if(tb <= vc-tc){
tc += tb;
tb = 0;
}else{
tb -= vc-tc;
tc = vc;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
}
if(c)
{
int ta = a, tb = b, tc = c;
if(tc <= va-ta){
ta += tc;
tc = 0;
}else{
tc -= va-ta;
ta = va;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
ta = a, tb = b, tc = c;
if(tc <= vb-tb){
tb += tc;
tc = 0;
}else{
tc -= vb-tb;
tb = vb;
}
if(!f.count({ta, tb, tc})) f[{ta, tb, tc}] = 1, que.push({ta, tb, tc});
}
}
}
signed main(){
Ios;
while(cin >> va >> vb >> vc)
{
bfs();
cout << mp.size() << endl;
}
return 0;
}
dfs,map映射哈希值,数组遍历处理操作:
#include<bits/stdc++.h>
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
/**/
const int N = 200010, mod = 1e9+7;
int T, n, m;
int c[3], v[3];
map<int, bool> f;
map<int, bool> mp;
int get(int c[])
{
return c[0]*5000*5000 + c[1]*5000 + c[2];
}
void pour(int c[], int x, int y)
{
if(c[x] >= v[y]-c[y])
{
c[x] -= v[y]-c[y];
c[y] = v[y];
}
else{
c[y] += c[x];
c[x] = 0;
}
}
void dfs(int c[])
{
int t[3];
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
if(i==j) continue;
memcpy(t, c, sizeof t);
pour(t, i, j);
if(f.count(get(t))) continue;
f[get(t)] = 1;
mp[t[2]] = 1;
dfs(t);
}
}
signed main(){
Ios;
while(cin >> v[0] >> v[1] >> v[2])
{
c[0] = c[1] = 0, c[2] = v[2];
f.clear();
mp.clear();
f[get(c)] = 1;
mp[c[2]] = 1;
dfs(c);
cout << mp.size() << endl;
}
return 0;
}