pku1184很有技巧的广搜

来源:互联网 发布:wifi稳定性测试软件 编辑:程序博客网 时间:2024/05/16 18:11
 

        题意:有一个六位数,开始光标在第一位数上,现在通过向左,右移动光标,将光标上的数加,减一,与第一,五个数交换这几个操作变为另一个数(光标在任何地方都满足),求最少的操作数。

       分析:开始想双向广搜,状态为6*10^6个状态,和单向广搜是一样的,会超时。后面想的几种优化发现根本就不叫优化。最后还是看了Discuss。。。orz,还是太水了。。。我们可以把两个数的六位数的对应关系枚举出来6!=720种情况,即目标的六个位置分别是原始的哪些位置上的数通过加一,减一而来的(因为光标左,右移,与第一,第五位置交换只是把数的位置换来换去)。又因为左,右移,与第一,五个位置交换也算操作,所以我们必须记录经过多少步可以到达这个对应关系。而转化的过程中与光标的位置相关,所以要标记现在光标的位置(6种),与光标已经经过的位置(我们把与第五个数交换也看作经过了第五个位置,10种情况)。

//输入两个六位数,问至少经过几次给定的交换使得第一个数等于第二个数//先将最终的数的位置对应的原来的位置求出来,后面直接算加还是减多少就行了#include<stdio.h>#include<iostream>using namespace std;struct point{int n;//排列int now;//现在的位置int s;//经过的位置}q[44000];//最终的每位数肯定都是由前面一个数的每位数对应变化而来的,所以对应关系最多为6!=720//第二维表示最终的光标停在哪个位置。第三维表示光标到过的位置,如果与六号位置交换了数字,则我们也算六号位置也经过了int dp[720][6][10];//dp[i][j][k]表示第i个排列,最终光标在j+1的位置,经过的位置为第k种情况//经过位置的具体情况int stage[10]={1,3,7,15,31,33,35,39,47,63};//用二进制表示各位,每位对应的权重为2^(i-1)。。。如39=1+2+4+32表示第1,2,3,6位置经过了,同时也说明有位置与6号位置交换过数字int A[10]={1,1,2,6,24,120,720,5040};int Abs(int n){return n>0? n:-n;}int C1(int a[])//对应的是第几个排列{int i,j,k,n=0;bool flag[6];memset(flag,false,sizeof(flag));for(i=0;i<5;i++){for(k=0,j=0;j<a[i];j++)if(!flag[j])k++;n+=k*A[5-i];flag[a[i]]=true;}return n;}void C2(int n,int a[])//第n个排列的每位对应多少{int i,j,k;bool flag[6];memset(flag,false,sizeof(flag));for(i=0;i<=5;i++){k=n/A[5-i];n=n%A[5-i];for(j=0;j<6;j++)if(!flag[j]){if(k==0){a[i]=j;flag[j]=true;break;}k--;}}}void C3(int n,int a[])//将n按位分离{int i=5;memset(a,0,6*sizeof(int));while(n){a[i]=n%10;n/=10;i--;}}void Init()//位置间的转化与输入的两个数是不相关的{int head=0,tail=0,step,n,now,s,i,k,s1,a1[6];memset(dp,-1,sizeof(dp));dp[0][0][0]=0;head=tail=0;q[tail].n=0;q[tail].now=0;q[tail++].s=0;while(head!=tail){n=q[head].n;now=q[head].now;s=q[head].s;step=dp[n][now][s];head++;//now位置左移if(now){if(dp[n][now-1][s]==-1){dp[n][now-1][s]=step+1;q[tail].n=n;q[tail].now=now-1;q[tail].s=s;tail++;}}//now位置右移,可能会经过新位置if(now!=5){k=stage[s];if((k&(1<<(now+1)))==0)//现在经过的是新位置{k=k+(1<<(now+1));for(s1=0;s1<10;s1++)if(stage[s1]==k)break;}else s1=s;if(dp[n][now+1][s1]==-1){dp[n][now+1][s1]=step+1;q[tail].n=n;q[tail].now=now+1;q[tail].s=s1;tail++;}}//与第一个数交换if(now){//排列会换C2(n,a1);i=a1[0];a1[0]=a1[now];a1[now]=i;i=C1(a1);if(dp[i][now][s]==-1){dp[i][now][s]=step+1;q[tail].n=i;q[tail].now=now;q[tail].s=s;tail++;}}//与最后一个数交换if(now!=5){C2(n,a1);i=a1[5];a1[5]=a1[now];a1[now]=i;i=C1(a1);k=stage[s];if((k&(1<<5))==0)//5以前没有经过{k=k+(1<<5);for(s1=0;s1<10;s1++)if(stage[s1]==k)break;}else s1=s;if(dp[i][now][s1]==-1){dp[i][now][s1]=step+1;q[tail].n=i;q[tail].now=now;q[tail].s=s1;tail++;}}}}int main(){int a,b,a1[10],a2[10],b1[10],i,j,k,ii,jj,kk;Init();while(scanf("%d%d",&a,&b)!=EOF){C3(a,a1);C3(b,b1);for(j=0,i=0;i<5;i++)if(a1[i]!=b1[i])j=i;for(k=0,i=0;i<=j;i++)//0至j必须经过k+=(1<<i);if(a1[5]!=b1[5]) k+=(1<<5);for(i=0;i<10;i++)if(stage[i]==k)break;for(ii=1110000,j=0;j<720;j++)for(k=0;k<6;k++)if(dp[j][k][i]!=-1){jj=dp[j][k][i];C2(j,a2);for(kk=0;kk<6;kk++)jj+=Abs(b1[kk]-a1[a2[kk]]);//换后a1[a2[kk]]对应b1[kk]if(jj<ii) ii=jj;}printf("%d\n",ii);}return 0;}


 

 

原创粉丝点击