Zju 1262 Word [HASH]解题报告

http://andyzh1314.ycool.com/post.1050667.html

 

Zju 1262 Word [HASH]解题报告

                                                               By AndyZhau

       单看此题是一个字符串处理的题目,其实不然。因为题目中只有ab,换种思路来讲,二进制中也只有01,并且串的长度最多为15。因此,此题第一个着眼点就是将串转化成数。为了方便后面的排序,定义0代表a1代表b

       接下来,面对题目中过大的S Solver们不免有些发愁,单纯的枚举超时无疑。这个时候Hash结构派上了用场。因为状态的数目最多也不过2^15

       Hash表我们可以开下,但是由于每次都要对Hash表清空,而题目的上千组数据严重的影响了时间效率。有两种解决办法:

       办法1:开散列表。(模函数法)

                     由于开散列表只需对每个桶进行清空,所以缩小了范围,减少了时间。

       办法2:两个索引数组。

第一个索引数组为Ans[]Ans [i] 表示执行了i不时的结果。

第二个索引数组为Pos[]Pos [t] 表示结果为t的伪执行次数。所谓伪执行次数就是说用Pos [t] Startlen 才是真正的执行次数。这里Startlen是对len的记录,而len是一个全局变量,每执行一次操作len++。而判断Pos [t]和不合法的方法就是判断Pos [t] 是否Startlen 大。

具体一些,假设在第i次运算之后取得了答案t。那么插入操作为:

       Ans [i] = t; // 记录状态

Pos [t] = len ++;

如果找到了一个已经出现的状态t,在第i次重复出现了,那么就已经能够确定答案了:

       if ( Pos [t] >= Startlen ) // 重复出现

因为产生了一个状态的环,所以答案就是第(S i % ( i Pos [t] + Startlen ) + Pos [t] Startlen Ans[]

       最后,题目要求输出字典序最小的,那么对答案做一次循环枚举就可以了。

       另外,也可以对先将每次产生的状态都转化为循环后的最小状态然后再插入Hash表,这样可以再提高一些效率。

代码1:开散列表。

#include <stdio.h>
#include <string.h>

const   int     Limit = 100000;
const   int     Hash = 88889;

int     N;
int     S;
int     Start;
int     Total;
int     pre [8];
char    data [17];
int     power2 [17];
int     Sum = 0;

int     len;
struct  node {
        int     Present , Pos , Next;
}       Data [Limit];
int     Bucket [Hash];

int     ord ( int N )
{
        int     i;
        int     ret = 0;
        for ( i = 0; i < N; i ++ ) {
                ret <<= 1;
                if ( data [i] == 'b' ) ret ++;
        }
        return ret;
}

bool    init ()
{
        if ( scanf ( "%d" , &N ) == EOF ) return false;

        int     i , t;

        scanf ( "%s" , data );
        Start = ord ( N );

        Total = 0;
        for ( i = 0; i < 8; i ++ ) {
                scanf ( "%s" , data );
                t = ord ( 3 );
                if ( data [3] == 'b' ) pre [t] = 1;
                        else pre [t] = 0;
                Total += pre [t];
        }
        scanf ( "%d" , &S );
        return true;
}

void    prepare ()
{
        int     i;
        power2 [0] = 1;
        for ( i = 1; i < 17; i ++ ) power2 [i] = power2 [i - 1] << 1;
}

void    print ( int N , int t )
{
        int     i;
        int     smallest = t;
        for ( i = 0; i < N; i ++ ) {
                if ( t & power2 [N - 1] ) t = (( t ^ power2 [N - 1] ) << 1 ) + 1;
                else t <<= 1;
                if ( t < smallest ) smallest = t;
        }

        for ( i = N - 1; i >= 0; i -- )
                if ( smallest & power2 [i] ) printf ( "b" );
                else printf ( "a" );
        printf ( ""n" );
}

int     find ( int x , int p )
{
        int     k;
        for ( k = Bucket [x % Hash]; k != -1; k = Data [k].Next )
                if ( Data [k].Present == x ) return k;
        k = x % Hash;
        Data [len].Next = Bucket [k];
        Data [len].Present = x;
        Data [len].Pos = p;
        Bucket [k] = len ++;
        return -1;
}
int     change ( int s )
{
        int     i , t;
        int     ret = 0;
        for ( i = 0; i < N; i ++ ) {
                t = 0;
                if ( s & power2 [i] ) t += 1;
                if ( s & power2 [( i + 1 ) % N] ) t += 2;
                if ( s & power2 [( i + 3 ) % N] ) t += 4;
                if ( pre [t] ) ret = ret | power2 [( i + 1 ) % N];
        }
        return ret;
}


int     Work ()
{
        Sum += S;
        if ( Total == 0 ) {
                if ( S > 1 ) return 0; else return S;
        }
        if ( Total == 8 ) {
                if ( S > 1 ) return power2 [N] - 1; else return S;
        }

        int     t = Start , b , i , j , e , f;
        int     ddd [30];
        for ( i = 0; i < 30; i ++ ) ddd [i] = i % N;

        len = 0;
        memset ( Bucket , 0xff , sizeof ( Bucket ));
        
        for ( i = 0; i < S; i ++ ) {
                f = find ( t , i );
                if ( f != -1 ) {
                        return Data [Data [f].Pos + ( S - i ) % ( i - Data [f].Pos )].Present;
                }

                e = 0;
                for ( j = 0; j < N; j ++ ) {
                        b = 0;
                        if ( t & power2 [ddd [j + 1]] ) b += 2;
                        if ( t & power2 [j] ) b += 1;
                        if ( t & power2 [ddd [j + 3]] ) b += 4;
                        if ( pre [b] ) e = e | power2 [ddd [j + 1]];
                }
                t = e;
        }
        return t;
}

main ()
{
        freopen ( "p.in" , "r" , stdin );
        freopen ( "p.out" , "w" , stdout );

        prepare ();
        while ( init () ) print ( N , Work () );
}

 

代码2:索引数组。

#include <stdio.h>
#include <string.h>

const   int     power2 [16] = { 1 , 2 , 4 , 8 , 16 ,
                                32 , 64 , 128 , 256 , 512 ,
                                1024 , 2048 , 4096 , 8192 , 16384 , 32768 };
                                
int     N , S;
int     Start , Total;

int     pre [8];
char    data [17];

int     len = 0;
int     Pos [1 << 15];
int     Ans [1 << 15];

int     ord ( int N )
{
        int     ret = 0;
        for ( int i = 0; i < N; i ++ ) {
                ret <<= 1;
                if ( data [i] == 'b' ) ret ++;
        }
        return ret;
}

bool    init ()
{
        if ( scanf ( "%d" , &N ) == EOF ) return false;

        int     i , t;
        scanf ( "%s" , data );
        Start = ord ( N );

        Total = 0;
        for ( i = 0; i < 8; i ++ ) {
                scanf ( "%s" , data );
                t = ord ( 3 );
                if ( data [3] == 'b' ) pre [t] = 1;
                        else pre [t] = 0;
                Total += pre [t];
        }
        scanf ( "%d" , &S );
        return true;
}

void    print ( int t )
{
        int     i;
        for ( i = N - 1; i >= 0; i -- )
                if ( t & power2 [i] ) printf ( "b" );
                else printf ( "a" );
        printf ( ""n" );
}

int     Smallest ( int t )
{
        int     i , ret = t;
        for ( i = 1; i < N; i ++ ) {
                if ( t & power2 [N - 1] ) t = (( t ^ power2 [N - 1] ) << 1 ) + 1;
                else t <<= 1;
                if ( t < ret ) ret = t;
        }
        return ret;
}

int     Work ()
{
        if ( Total == 0 ) {
                if ( S > 1 ) return 0; else return S;
        }
        if ( Total == 8 ) {
                if ( S > 1 ) return power2 [N] - 1; else return S;
        }

        int     i , Num [18];
        for ( i = 0; i < N + 3; i ++ ) Num [i] = i % N;

        int     j , Startlen = len , t = Smallest ( Start ) , Next , PreIndex;
        for ( i = 0; i < S; i ++ ) {
                if ( Pos [t] >= Startlen )
                        return Ans [( S - i ) % ( i - Pos [t] + Startlen ) + Pos [t] - Startlen];
                Ans [i] = t; Pos [t] = len ++;
                Next = 0;
                for ( j = 0; j < N; j ++ ) {
                        PreIndex = 0;
                        if ( t & power2 [Num [j]] ) PreIndex = 1;
                        if ( t & power2 [Num [j + 1]] ) PreIndex += 2;
                        if ( t & power2 [Num [j + 3]] ) PreIndex += 4;
                        if ( pre [PreIndex] ) Next = Next | power2 [Num [j + 1]];
                }
                t = Smallest ( Next );
        }
        return  t;
}

main ()
{
        memset ( Pos , 0xff , sizeof ( Pos ));
        while ( init () ) print ( Work () );
}

 

posted @ 2008-12-05 14:51  jesonpeng  阅读(152)  评论(0)    收藏  举报