如果用bfs的话,也即,把操作顺序的先后考虑上的话,那么要注意用hash表来判重,用空间换时间。这个低效的方法也是能过的……
//hash表判重,dfs,从短到长按字典序扩展操作序列的长度
/*
{
ID: lzwjava1
PROG: clocks
LANG: C++
}
*/
#include<cstdio>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int MaxState=500000;
int move[9][5]={{0,1,3,4},{0,1,2},{1,2,4,5},{0,3,6},
{1,3,4,5,7},{2,5,8},{3,4,6,7},{6,7,8},{4,5,7,8}};
int Nmove[9]={4,3,4,3,5,3,4,3,4};
typedef char state[9];
state st[MaxState];
char oper[MaxState];
bool isGoal(state a)
{
for(int i=0;i<9;i++)
if(a[i]) return false;
return true;
}
int fa[MaxState];
void doOper(state &a,int op)
{
for(int i=0;i<Nmove[op];i++)
++a[move[op][i]]%=4;
}
const int HashSize=10007;
int head[HashSize];
int next[MaxState];
int value[10];
int hash(int u)
{
int t=0;
for(int i=9;i>=0;i--)//mistook:i++,it should be"i--"
t+=value[9-i]*st[u][i];
return t%HashSize;
}
bool tryToInsert(int u)
{
int h=hash(u);
int p=head[h];
while(p!=-1)
{
if(memcmp(st[p],st[u],sizeof(st[p]))==0) return false;
p=next[p];
}
next[u]=head[h];
head[h]=u;
return true;
}
void init_hash()
{
memset(head,-1,sizeof(head));
value[0]=1;
for(int i=1;i<10;i++)
value[i]=value[i-1]*4;
}
int bfs()
{
int front,rear;
front=0;rear=1;
fa[front]=-1;
init_hash();
while(front<rear)
{
state &u=st[front];
if(isGoal(u)) return front;
for(int i=0;i<9;i++)
{
state &v=st[rear];
memcpy(v,u,sizeof(v));
doOper(v,i);
if(tryToInsert(rear))
{
fa[rear]=front;
oper[rear++]=i;
}
}
front++;
}
}
void print_ans(int u)
{
if(fa[u]==-1) return;
print_ans(fa[u]);
if(fa[u]!=0) printf(" ");
printf("%d",oper[u]+1);
}
int main()
{
freopen("clocks.in","r",stdin);
freopen("clocks.out","w",stdout);
int i,j;
for(i=0;i<9;i++)
{
int t;
scanf("%d",&t);
st[0][i]=t/3%4;
//printf("%d\n",st[0][i]);
}
int ans=bfs();
//printf("%d\n",ans);
print_ans(ans);printf("\n");
return 0;
}
/*
*/
事实上,操作的先后并不重要。用dfs降低空间复杂度,但最坏的时间复杂度是一样的。4^9。bfs找到解即返回。
//dfs,无所谓操作的先后,而只记次数。遍历每一种可能的情况。
/*
{
ID: lzwjava1
PROG: clocks
LANG: C++
}
*/
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;
const int MaxMove=27;
char move[9][9]={"ABDE","ABC","BCEF",//直接复制粘贴题目,在程序中来得到操作矩阵
"ADG","BDEFH","CFI","DEGH","GHI","EFHI"};
int A[9];
int best[9];//最优答案
int nbest;//最优答案的序列长度。先要最短,然后字典序最小
int Clock[9][9];//Clock[i][j]为操作i一次j需要转过的刻钟的数目,3表示90°,0表示0°。只有这两种情况
FILE *in,*out;
void cal_Clock()
{
memset(Clock,0,sizeof(Clock));
char *p;
for(int i=0;i<9;i++)
for(p=move[i];*p;p++)
Clock[i][*p-'A']=3;
}
bool is_ok(int *A)
{
for(int i=0;i<9;i++)
if(A[i]!=12) return false;
return true;
}
void solve(int *m,int cur)
{
int i,j;
if(cur==9)
{
if(is_ok(A))
{
int t=0;
for(i=0;i<cur;i++) t+=m[i];
if(!nbest || t<nbest)
{
memcpy(best,m,sizeof(best));
nbest=t;
}
}
return;//mistook:没加
}
for(i=3;i>=0;i--)//先3个小序号的操作,这样可以得到最小字典序的方案
{
for(j=0;j<i;j++)
for(int k=0;k<9;k++)
{
A[k]+=Clock[cur][k];
if(A[k]==15) A[k]=3;
//fprintf(out,"A[k]=%d\n",A[k]);
}
m[cur]=i;
solve(m,cur+1);
for(j=0;j<i;j++)
for(int k=0;k<9;k++)
{
A[k]-=Clock[cur][k];
if(A[k]==0) A[k]=12;
}
}
}
void testClock()
{
int i,j;
for(i=0;i<9;i++,printf("\n"))
for(j=0;j<9;j++)
{
printf(" %d",Clock[i][j]);
}
}
int main()
{
in=fopen("clocks.in","r");
out=fopen("clocks.out","w");
int i,j;
for(i=0;i<9;i++) fscanf(in,"%d",&A[i]);
//for(i=0;i<9;i++) printf("%d ",A[i]);
int m[9];//m为次数数组,m[i]为执行操作i的次数
cal_Clock();
//testClock();
nbest=0;
solve(m,0);
bool first=true;
for(i=0;i<9;i++)
if(best[i])
{
for(j=0;j<best[i];j++)
{
if(!first) fprintf(out," ");
else first=false;
fprintf(out,"%d",i+1);
}
}
fprintf(out,"\n");
exit(0);
}
还可以通过打表得到最优解。这下可以优化到常数时间。
简便起见,把3,6,9,10表示成0,1,2,3。现在得到一个数组A,有9个操作,记第i个操作为O[i],O[i]让数组A中的某些数向前增加1,超过3则变为0。求一个最短的操作序列,让数组A的9个元素都变为0。
记V[j]为在最终的操作序列中执行O[j]的次数。
a[i][j],表示仅让A[i]向前增1,而其它数都不变,所要执行O[j]的次数。得到矩阵a。矩阵a可以通过dfs的方法得到。
先求得a数组。改造一下上面的程序即可。
//dfs,巧妙得到a数组
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;
const int MaxMove=27;
char move[9][9]={"ABDE","ABC","BCEF",
"ADG","BDEFH","CFI","DEGH","GHI","EFHI"};
int A[9];
int best[9];
int nbest;
int Clock[9][9];
FILE *in,*out;
void cal_Clock()
{
memset(Clock,0,sizeof(Clock));
char *p;
for(int i=0;i<9;i++)
for(p=move[i];*p;p++)
Clock[i][*p-'A']=3;
}
bool is_ok(int *A)
{
for(int i=0;i<9;i++)
if(A[i]!=12) return false;
return true;
}
void solve(int *m,int cur)
{
int i,j;
if(cur==9)
{
if(is_ok(A))
{
int t=0;
for(i=0;i<cur;i++) t+=m[i];
if(!nbest || t<nbest)
{
memcpy(best,m,sizeof(best));
nbest=t;
}
}
return;//mistook:没加
}
for(i=3;i>=0;i--)
{
for(j=0;j<i;j++)
for(int k=0;k<9;k++)
{
A[k]+=Clock[cur][k];
if(A[k]==15) A[k]=3;
//fprintf(out,"A[k]=%d\n",A[k]);
}
m[cur]=i;
solve(m,cur+1);
for(j=0;j<i;j++)
for(int k=0;k<9;k++)
{
A[k]-=Clock[cur][k];
if(A[k]==0) A[k]=12;
}
}
}
void testClock()
{
int i,j;
for(i=0;i<9;i++,printf("\n"))
for(j=0;j<9;j++)
{
printf(" %d",Clock[i][j]);
}
}
void work()
{
//in=fopen("clocks.in","r");
int i,j;
//for(i=0;i<9;i++) printf("%d ",A[i]);
int m[9];
//testClock();
nbest=0;
solve(m,0);
bool first=true;
fprintf(out,"{%d",best[0]);
for(i=1;i<9;i++)
fprintf(out,",%d",best[i]);
fprintf(out,"},\n");
}
int main()
{
out=fopen("a.out","w");
int i,j;
for(i=0;i<9;i++) A[i]=12;
cal_Clock();
for(i=0;i<9;i++)
{
A[i]=9;
work();//得到尽让钟i转过90度的最优序列
A[i]=12;
}
exit(0);
}
接下来,神代码出现了:
//dfs下得到预处理数组。情况有限,
/*
{
ID: lzwjava1
PROG: clocks
LANG: C++
}
*/
#include<cstdio>
#include<cstring>
int a[9][9]={
{3,3,3,3,3,2,3,2,0},
{2,3,2,3,2,3,1,0,1},
{3,3,3,2,3,3,0,2,3},
{2,3,1,3,2,0,2,3,1},
{2,3,2,3,1,3,2,3,2},
{1,3,2,0,2,3,1,3,2},
{3,2,0,3,3,2,3,3,3},
{1,0,1,3,2,3,2,3,2},
{0,2,3,2,3,3,3,3,3}
};
int main()
{
freopen("clocks.in","r",stdin);
freopen("clocks.out","w",stdout);
int v[9];//v[i]表示操作i的执行次数
memset(v,0,sizeof(v));
int i,j,k;
for(i=0;i<9;i++)
{
scanf("%d",&k);
for(j=0;j<9;j++)//计算把第i个钟的k变为12 v数组的变化。
v[j]=(v[j]+a[i][j]*(12-k)/3)%4;//转一次,要执行j操作a[i][j]次
}
bool first=true;
for(i=0;i<9;i++)
if(v[i])
{
for(j=0;j<v[i];j++)
{
if(first) first=false;
else printf(" ");
printf("%d",i+1);
}
}
printf("\n");
return 0;
}
如果不是很理解,见:
Lucian Boca submitted a constant time solution
You can precalculate a matrix a as following:
a[i][0],a[i][1],....,a[i][8] is the number of moves '1','2','3',...'9' necessarly to move ONLY clock i (where 0 < i <= 8 - there are 9 clocks: 0=A, 1=B, ... 8=I) 90 degrees clockwise. So, you have the matrix:
int a[9][9]= { {3,3,3,3,3,2,3,2,0},
{2,3,2,3,2,3,1,0,1},
{3,3,3,2,3,3,0,2,3},
{2,3,1,3,2,0,2,3,1},
{2,3,2,3,1,3,2,3,2},
{1,3,2,0,2,3,1,3,2},
{3,2,0,3,3,2,3,3,3},
{1,0,1,3,2,3,2,3,2},
{0,2,3,2,3,3,3,3,3} };
That means: to move ONLY the clock 0 (clock A) 90 degrees clockwise you have to do {3,3,3,3,3,2,3,2,0}, 3 moves of type 1, three moves of type 2, ..., 2 moves of type 8, 0 moves of type 9, etc.
To move ONLY the clock 8 (clock I), you have to do the moves {0,2,3,2,3,3,3,3,3}: 0 moves of type 1, 2 moves of type 2... 3 moves of type 9....
That's it! You count in a vector v[9] how many moves of each type you have to do, and the results will be modulo 4 (%4 - 5 moves of any type have the same effect 1 move has).