题目:两瓶 8 两的酒,一个 3 两的杯子,瓶和杯子都没有刻度,如何将酒平均分给四个人喝?也就是每个人分 4 两酒,也没什么限制条件。
当然,喝过的酒不能再吐出来(: > )。
回溯法解法(我看到的)
http://blog.csdn.net/clearriver/article/details/4418611?utm_source=jiancoolfor (int i=0;i<3;i++)
for(int j=0;j<7;j++)
{
int *px=new int[7];
for (int k=0;k<7;k++)
px[k]=pp[k];
n=play(px,i,j);
if (n!=0)
{
minso=min(minso,solve(px,nowstep+1));
if (minso!=frontm)
{
for (int k=0;k<7;k++)
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][k]=px[k];
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][7]=i+1;
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][8]=j+1;
}
frontm=minso;
}
}
还有一个问题就是,这个问题和一般的dp不一样,并没有很明确的转移方向,所以类似宽搜(当年我高中的时候似乎用宽搜写过类似的题,所以这题应该也可以用宽搜,而且效率应该会更高。不过用dp的话可以顺手练习一下dp,因为似乎dp除了竞赛还是很少用的。)的方式记录正在被处理的点。
其实写算法的时候还有一个很神奇的问题,就是这句话
if ((solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]!=0)&&(timestep[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]<=nowstep))
{
return solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]];
}
因为可能出现一个本来在后面的状态先被找到,然后再找到原本在前面的那个状态,然后本来在前面的那个状态在找寻的时候可能由于原本后面的那个状态现在正在被寻找不能找回去所以没有找到正确解。然后当本来该在前面的那个状态被找到的时候,由于已经被找过了,所以原本最优的解会被漏过(原本的最优解变成了无解,因为被略过的那个状态并没有被重新找过。)。所以记录到达这种状态所需的步数,如果更快的到达了某个状态,就重新计算以刷新它,这样就不会漏过解了,但是时间会变得复杂一些。
额,比较特别的地方应该就是这几个,然后上代码。熬夜赶出来的所以不是很精简,也懒得优化了。
#include
#include
using namespace std;
int solution[9][9][4][5][5][5][5];//用来记录某个状态到终点的最低步数
int timestep[9][9][4][5][5][5][5];//用来记录到达某个状态的最低步数
bool doing[9][9][4][5][5][5][5];//用来记录某个状态是否正在被执行
int answer[9][9][4][5][5][5][5][9];//用来记录到达某个状态之后的状态,和到达这个状态的操作
int min(int a,int b)//求最小值,经常用到
{
if(a>b)
return b;
return a;
}
int pp(int a,int b)//瓶子到瓶子
{
return min(a,8-b);
}
int pc(int a,int b)//瓶子到杯子
{
return min(a,3-b);
}
int cp(int a,int b)//杯子到瓶子
{
return min(a,8-b);
}
int pr(int a,int b)//瓶子到人
{
if (a<=(4-b))
{
return a;
}
return 0;
}
int cr(int a,int b)//杯子到人
{
if (a<=(4-b))
{
return a;
}
return 0;
}
int play(int num[],int b1,int b2)//对于某个状态的,试着用b1号容器往b2号容器里倒
{
if(b1<2)
{
if (b2<2)
{
int j=pp(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
else
{
return 0;
}
}
if (b2==2)
{
int j=pc(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
if (b2>2)
{
int j=pr(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
}
if (b1==2)
{
if (b2<2)
{
int j=cp(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
if (b2>2)
{
int j=cr(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
}
return 0;
}
int solve(int pp[],int nowstep)//大家都懂的dp
{
if ((solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]!=0)&&(timestep[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]<=nowstep))
{
return solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]];
}
if ((pp[3]==4)&&(pp[4]==4)&&(pp[5]==4)&&(pp[6]==4))
{
return 0;
}
if (doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]])
{
return 100000;
}
doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=true;
int minso=100000;
int frontm=100000;
int n=0;
for (int i=0;i<3;i++)
for(int j=0;j<7;j++)
{
int *px=new int[7];
for (int k=0;k<7;k++)
px[k]=pp[k];
n=play(px,i,j);
if (n!=0)
{
minso=min(minso,solve(px,nowstep+1));
if (minso!=frontm)
{
for (int k=0;k<7;k++)
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][k]=px[k];
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][7]=i+1;
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][8]=j+1;
}
frontm=minso;
}
}
doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=false;
timestep[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=nowstep;
solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=minso+1;
return minso+1;
}
int main()
{
memset(solution,0,sizeof(solution));
memset(doing,0,sizeof(doing));
memset(timestep,0,sizeof(timestep));
memset(answer,0,sizeof(answer));
// cout<<(sizeof(solution)+sizeof(doing)+sizeof(answer))/1024<"<< answer[p1][p2][b][r1][r2][r3][r4][8]<
#include
#include
using namespace std;
int solution[9][9][4][5][5][5][5];//用来记录某个状态到终点的最低步数
//int timestep[9][9][4][5][5][5][5];//用来记录到达某个状态的最低步数
bool doing[9][9][4][5][5][5][5];//用来记录某个状态是否正在被执行
int answer[9][9][4][5][5][5][5][9];//用来记录到达某个状态之后的状态,和到达这个状态的操作
int min(int a,int b)//求最小值,经常用到
{
if(a>b)
return b;
return a;
}
int pp(int a,int b)//瓶子到瓶子
{
return min(a,8-b);
}
int pc(int a,int b)//瓶子到杯子
{
return min(a,3-b);
}
int cp(int a,int b)//杯子到瓶子
{
return min(a,8-b);
}
int pr(int a,int b)//瓶子到人
{
if (a<=(4-b))
{
return a;
}
return 0;
}
int cr(int a,int b)//杯子到人
{
if (a<=(4-b))
{
return a;
}
return 0;
}
int play(int num[],int b1,int b2)//对于某个状态的,试着用b1号容器往b2号容器里倒
{
if(b1<2)
{
if (b2<2)
{
int j=pp(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
if (b2==2)
{
int j=pc(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
if (b2>2)
{
int j=pr(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
}
if (b1==2)
{
if (b2<2)
{
int j=cp(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
if (b2>2)
{
int j=cr(num[b1],num[b2]);
if (j!=0)
{
num[b1]-=j;
num[b2]+=j;
return j;
}
}
}
return 0;
}
int solve(int pp[],int nowstep)//大家都懂的dp
{
if (solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]!=0)
{
return solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]];
}
if ((pp[3]==4)&&(pp[4]==4)&&(pp[5]==4)&&(pp[6]==4))
{
return 0;
}
if (doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]])
{
return 100000;
}
doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=true;
int minso=100000;
int frontm=100000;
int n=0;
for (int i=0;i<3;i++)
for(int j=0;j<7;j++)
{
int *px=new int[7];
for (int k=0;k<7;k++)
px[k]=pp[k];
n=play(px,i,j);
if((i<2)&&(j<2)&&((px[i]==8)||(px[j]==8)))
{
n=0;
//cout<<"back"< }
if (n!=0)
{
minso=min(minso,solve(px,nowstep+1));
if (minso!=frontm)
{
for (int k=0;k<7;k++)
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][k]=px[k];
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][7]=i+1;
answer[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]][8]=j+1;
}
frontm=minso;
}
}
doing[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=false;
// timestep[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=nowstep;
solution[pp[0]][pp[1]][pp[2]][pp[3]][pp[4]][pp[5]][pp[6]]=minso+1;
return minso+1;
}
int main()
{
memset(solution,0,sizeof(solution));
memset(doing,0,sizeof(doing));
// memset(timestep,0,sizeof(timestep));
memset(answer,0,sizeof(answer));
// cout<<(sizeof(solution)+sizeof(doing)+sizeof(answer))/1024< int *sr=new int[7];
sr[0]=8;
sr[1]=8;
sr[2]=0;
sr[3]=0;
sr[4]=0;
sr[5]=0;
sr[6]=0;//因为用数组存数据,所以准备有点麻烦
cout<
//开始输出路径
int p1=8;
int p2=8;
int b=0;
int r1=0;
int r2=0;
int r3=0;
int r4=0;
cout<
while (!((r1==4)&&(r2==4)&&(r3==4)&&(r4==4)))
{
cout<"<< answer[p1][p2][b][r1][r2][r3][r4][8]< int tp1=answer[p1][p2][b][r1][r2][r3][r4][0];
int tp2=answer[p1][p2][b][r1][r2][r3][r4][1];
int tb=answer[p1][p2][b][r1][r2][r3][r4][2];
int tr1=answer[p1][p2][b][r1][r2][r3][r4][3];
int tr2=answer[p1][p2][b][r1][r2][r3][r4][4];
int tr3=answer[p1][p2][b][r1][r2][r3][r4][5];
int tr4=answer[p1][p2][b][r1][r2][r3][r4][6];
p1=tp1;
p2=tp2;
b=tb;
r1=tr1;
r2=tr2;
r3=tr3;
r4=tr4;
cout<
}
return 0;
}
快了很多,但是为什么呢?现在大概只能说是一个巧合吧。
因为没有什么注释的好习惯,又是第一次写博客,可能写的不是很清楚,希望能帮助到各位对dp不太熟悉的人。什么,你精通dp?你是算法大牛?能看到这里真是有耐心。欢迎加qq3220665887,一起讨论算法或者编程问题。