USACO都荒废好久了,今年都没怎么做,今天突然想做做
一道搜索题,写了个很**的算法,开了9维的数组保存状态,能写成这样也不容易啊,时间复杂度可想而知,
因为用的广搜,空间就更。。。
一看解题报告,太伤人了,居然有O(1)的算法!
/*
ID: superbi1
PROG: clocks
LANG: C++
*/
#include < iostream >
#include < string .h >
#include < fstream >
#include < queue >
using namespace std;
const int MAX = 1 << 18 ;
struct Node {
int state[ 10 ];
int que[ 30 ];
int N;
};
Node start;
bool flg[ 4 ][ 4 ][ 4 ][ 4 ][ 4 ][ 4 ][ 4 ][ 4 ][ 4 ];
int dir[ 10 ][ 7 ] = {
{ 0 },
{ 4 , 1 , 2 , 4 , 5 },
{ 3 , 1 , 2 , 3 },
{ 4 , 2 , 3 , 5 , 6 },
{ 3 , 1 , 4 , 7 },
{ 5 , 2 , 4 , 5 , 6 , 8 },
{ 3 , 3 , 6 , 9 },
{ 4 , 4 , 5 , 7 , 8 },
{ 3 , 7 , 8 , 9 },
{ 4 , 5 , 6 , 8 , 9 }
};
bool OK(Node cur)
{
for ( int I = 1 ; I <= 9 ; I ++ ) {
if (cur.state[I] != 12 ) return false ;
}
return true ;
}
void chg(Node & nd1, int I)
{
int K;
for (K = 1 ; K <= dir[I][ 0 ]; K ++ ) {
int t = nd1.state[dir[I][K]];
t += 3 ;
if (t > 12 ) t %= 12 ;
nd1.state[dir[I][K]] = t;
}
}
bool doflg(Node nd1)
{
int t[ 9 ];
for ( int I = 1 ; I <= 9 ; I ++ ) {
t[I - 1 ] = nd1.state[I] / 3 - 1 ;
}
if (flg[t[ 0 ]][t[ 1 ]][t[ 2 ]][t[ 3 ]][t[ 4 ]][t[ 5 ]][t[ 6 ]][t[ 7 ]][t[ 8 ]]) return false ;
flg[t[ 0 ]][t[ 1 ]][t[ 2 ]][t[ 3 ]][t[ 4 ]][t[ 5 ]][t[ 6 ]][t[ 7 ]][t[ 8 ]] = 1 ;
return true ;
}
Node BFS()
{
queue < Node > que;
Node cur, nxt;
memset(flg, 0 , sizeof (flg));
que.push(start);
doflg(start);
int I;
while ( ! que.empty()) {
cur = que.front();
que.pop();
if (OK(cur)) { return cur; }
for (I = 1 ; I <= 9 ; I ++ ) {
nxt = cur;
chg(nxt, I);
if ( ! doflg(nxt)) continue ;
nxt.que[nxt.N] = I;
(nxt.N) ++ ;
que.push(nxt);
}
}
return cur;
}
int main()
{
ofstream fout( " clocks.out " );
ifstream fin( " clocks.in " );
int I;
Node ans;
for (I = 1 ; I <= 9 ; I ++ ) {
fin >> start.state[I];
}
start.N = 0 ;
ans = BFS();
for (I = 0 ; I < ans.N; I ++ ) {
if (I) fout << ' ' ;
fout << ans.que[I];
}
fout << endl;
return 0 ;
}
来看看解题报告吧:
Notice that the order in which we apply moves is irrelevant, and that applying a move four times is the same as applying it not at all.
Thus there are only 49 = 262144 move sequences that need to be tried, so we might as well just try them all.
We don't generate them shortest first, but looking at sequences of the same length, we generate the lesser ones before the greater ones, so we only need to keep track of the shortest working sequence we've found.
#include < stdio.h >
#include < stdlib.h >
#include < string .h >
#include < assert.h >
#include < ctype.h >
#define INF 60000 /* bigger than longest possible path */
char * movestr[] = { " abde " , " abc " , " bcef " , " adg " , " bdefh " , " cfi " , " degh " ,
" ghi " , " efhi " };
int movedist[ 9 ][ 9 ];
int clock[ 9 ];
int bestmove[ 9 ];
int nbestmove;
/* translate move strings into array "movedist" of which clocks change on each move */
void
mkmove( void )
{
int i;
char * p;
for (i = 0 ; i < 9 ; i ++ )
for (p = movestr[i]; * p; p ++ )
movedist[i][ * p - ' a ' ] = 3 ;
}
/* apply some number of each move from k to 9 */
/* move contains the number of times each move is applied */
void
solve( int * move, int k)
{
int i, j, n, rep;
if (k == 9 ) {
for (j = 0 ; j < 9 ; j ++ )
if (clock[j] % 12 != 0 )
return ;
/* we have a successful sequence of moves */
n = 0 ;
for (j = 0 ; j < 9 ; j ++ )
n += move[j];
if (nbestmove == 0 || n < nbestmove) {
nbestmove = n;
for (j = 0 ; j < 9 ; j ++ )
bestmove[j] = move[j];
}
return ;
}
/*
* the for loop counts down so we
* generate smaller numbers first by
* trying more of small numbered
* moves before we try less of them.
*/
for (rep = 3 ; rep >= 0 ; rep -- ) {
/* apply move k rep times */
for (i = 0 ; i < rep; i ++ )
for (j = 0 ; j < 9 ; j ++ )
clock[j] += movedist[k][j];
move[k] = rep;
solve(move, k + 1 );
/* undo move */
for (i = 0 ; i < rep; i ++ )
for (j = 0 ; j < 9 ; j ++ )
clock[j] -= movedist[k][j];
}
}
void
main( void )
{
FILE * fin, * fout;
int i, j, move[ 9 ];
char * sep;
fin = fopen( " clocks.in " , " r " );
fout = fopen( " clocks.out " , " w " );
assert(fin != NULL && fout != NULL);
mkmove();
for (i = 0 ; i < 9 ; i ++ )
fscanf(fin, " %d " , & clock[i]);
solve(move, 0 );
sep = "" ;
for (i = 0 ; i < 9 ; i ++ ) {
for (j = 0 ; j < bestmove[i]; j ++ ) {
fprintf(fout, " %s%d " , sep, i + 1 );
sep = " " ;
}
}
fprintf(fout, " \n " );
exit( 0 );
}
再看看最牛叉的代码:
算法思想就是:
先计算出只将每个clock顺时针转90的最少的move 序列,将每一个clock转至12点,得到一个操作序列v[i],由于转4次等于没转,再mod 4 就OK了!
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). The source code:
#include < stdio.h >
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 v[ 9 ];
int main() {
int i,j,k;
freopen( " clocks.in " , " r " ,stdin);
for (i = 0 ; i < 9 ; i ++ ) {
scanf( " %d " , & k);
for (j = 0 ; j < 9 ; j ++ )
v[j] = (v[j] + ( 4 - k / 3 ) * a[i][j]) % 4 ;
}
fclose(stdin);
k = 0 ;
freopen( " clocks.out " , " w " ,stdout);
for (i = 0 ; i < 9 ; i ++ )
for (j = 0 ; j < v[i]; j ++ )
if ( ! k) { printf( " %d " ,i + 1 ); k = 1 ; }
else printf( " %d " ,i + 1 );
printf( " \n " );
fclose(stdout);
return 0 ;
}