题意:给你3个杯子,容量分别为S,N,M,其中S=N+M,三个数都是正整数.一开始S是满的,N和M都是空的,杯子都没刻度.最少倒几次能使其中两个杯子平分可乐.
这道题是个BFS的入门题,解法很容易想到:
-
杯子都没刻度,说明A杯往B杯倒可乐,只有2种情况:把B倒满,或者把A倒空,所以我们就可以得到2个新的状态,把它们加入广搜队列;当然A->B有6种排列方式,所以总共要写十二次倒可乐的状态转移操作.
-
最少倒几次能使其中两个杯子平分可乐:也就是说第一次出现两个杯子的可乐量等于s/2时的操作次数.可以知道s % 2=1的话是肯定不可能有解的.
在此,主要用这个题练练STL:如何用STL方便地帮助搜索
注意:下面这种tuple、map的用法,在某些搜索的情景下是低效的,完全可以用struct和三维数组高效代替,此处只是练习STL
1. 表示当前可乐容量状态的三元组:
可以用含有三个元素的结构体存储状态,并且把它入队.STL里的tuple可以方便地存储一个多元组,这样就不用结构体啦:
定义一个三元组:
tuple<int,int,int>head;
获取该状态下的三个元素:
x=get<0>(head); y=get<1>(head); z=get<2>(head);
给这个组添加元素,比如s,0,0:
head=make_tuple(s,0,0);
2. 当前可乐容量状态对应的已经进行的倾倒次数:
自然而然想到map了
map<tuple<int,int,int>,int>dist;
3. 某一状态是否已经被访问(搜索)过:
这个同上,其实完全可以用上面的dist代替:假如没被搜过,它对应的次数就是0啦
4.把map和tuple初始化为空,用.clear()函数就可以了
(tuple+map)AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100+5;
const int MAXM=0+5;
const int INF=0x3f3f3f3f;
int s,n,m;
queue<tuple<int,int,int> >q;
map<tuple<int,int,int>,int>dist;
tuple<int,int,int>tmp;
tuple<int,int,int>head;
bool daoman(int i,int j,int p) {
return (i>=p-j)?1:0;
}
bool daokong(int i,int j,int p) {
return (i+j<=p)?1:0;
}
void doit() {
q.push(tmp);
dist[tmp]=dist[head]+1;
}
void bfs() {
head=make_tuple(s,0,0);
q.push(head);
int mn=INF;
while(q.size()) {
head=q.front();
q.pop();
int x=get<0>(head),y=get<1>(head),z=get<2>(head);
if((x==s/2&&y==s/2)||(x==s/2&&z==s/2))mn=min(mn,dist[head]);
if(mn!=INF) {
cout<<mn<<'\n';
break;
}
if(daoman(x,y,n)) {
tmp=make_tuple(x-(n-y),n,z);
if(!dist[tmp]) doit();
}
if(daoman(x,z,m)) {
tmp=make_tuple(x-(m-z),y,m);
if(!dist[tmp]) doit();
}
if(daoman(y,z,m)) {
tmp=make_tuple(x,y-(m-z),m);
if(!dist[tmp]) doit();
}
if(daoman(y,x,s)) {
tmp=make_tuple(s,y-(s-x),z);
if(!dist[tmp])doit();
}
if(daoman(z,x,s)) {
tmp=make_tuple(s,y,z-(s-x));
if(!dist[tmp]) doit();
}
if(daoman(z,y,n)) {
tmp=make_tuple(x,n,z-(n-y));
if(!dist[tmp]) doit();
}
if(daokong(x,y,n)) {
tmp=make_tuple(0,x+y,z);
if(!dist[tmp]) doit();
}
if(daokong(x,z,m)) {
tmp=make_tuple(0,y,x+z);
if(!dist[tmp])doit();
}
if(daokong(y,z,m)) {
tmp=make_tuple(x,0,y+z);
if(!dist[tmp]) doit();
}
if(daokong(y,x,s)) {
tmp=make_tuple(x+y,0,z);
if(!dist[tmp]) doit();
}
if(daokong(z,x,s)) {
tmp=make_tuple(x+z,y,0);
if(!dist[tmp]) doit();
}
if(daokong(z,y,n)) {
tmp=make_tuple(x,y+z,0);
if(!dist[tmp]) doit();
}
}
if(mn==INF)cout<<"NO"<<'\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
while(cin>>s>>n>>m) {
if(!s&&!n&&!m)break;
while(q.size())q.pop();
dist.clear();
if(s%2)cout<<"NO"<<'\n';
else bfs();
}
return 0;
}
(结构体+三维数组)AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100+5;
const int MAXM=0+5;
const int INF=0x3f3f3f3f;
int s,n,m;
int x,y,z;
int dist[MAXN][MAXN][MAXN];
struct node {
int x,y,z;
} head,tmp;
queue<struct node>q;
bool daoman(int i,int j,int p) {
return (i>=p-j)?1:0;
}
bool daokong(int i,int j,int p) {
return (i+j<=p)?1:0;
}
void doit(int xx,int yy,int zz) {
if(dist[xx][yy][zz])return;
tmp.x=xx,tmp.y=yy,tmp.z=zz;
q.push(tmp);
dist[xx][yy][zz]=dist[x][y][z]+1;
}
void bfs() {
head.x=s,head.y=0,head.z=0;
q.push(head);
int mn=INF;
while(q.size()) {
head=q.front();
q.pop();
x=head.x,y=head.y,z=head.z;
if((x==s/2&&y==s/2)||(x==s/2&&z==s/2))mn=min(mn,dist[x][y][z]);
if(mn!=INF) {
cout<<mn<<'\n';
break;
}
if(daoman(x,y,n))doit(x-(n-y),n,z);
if(daoman(x,z,m))doit(x-(m-z),y,m);
if(daoman(y,z,m))doit(x,y-(m-z),m);
if(daoman(y,x,s))doit(s,y-(s-x),z);
if(daoman(z,x,s))doit(s,y,z-(s-x));
if(daoman(z,y,n))doit(x,n,z-(n-y));
if(daokong(x,y,n))doit(0,x+y,z);
if(daokong(x,z,m))doit(0,y,x+z);
if(daokong(y,z,m))doit(x,0,y+z);
if(daokong(y,x,s))doit(x+y,0,z);
if(daokong(z,x,s))doit(x+z,y,0);
if(daokong(z,y,n))doit(x,y+z,0);
}
if(mn==INF)cout<<"NO"<<'\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
while(cin>>s>>n>>m) {
if(!s&&!n&&!m)break;
memset(dist,0,sizeof(dist));
while(q.size())q.pop();
if(s%2)cout<<"NO"<<'\n';
else bfs();
}
return 0;
}