HDU1536,SG函数(①打表,②回溯),简单应用示例2

来源:互联网 发布:linux 文件夹访问权限 编辑:程序博客网 时间:2024/06/05 10:49

0

1

将游戏拆分成多个子游戏,求各自的sg值再异或。

如何求sg函数值?如果查询次数过多,可以预先打表,然后查;如果查询次数相对不多,但是题目卡时间,可以回溯法,求哪个查哪个。

打表还有一个好处,把所有可能用到的sg值都求了出来,如果对时间有进一步要求的题目,可以打表以后寻找sg函数值的规律,直接根据规律用数学式子求sg值,以达到O(1)的sg查询速度。

回溯的好处是在于不需要找规律,且在线查询sg值,查询时间略快于打表,因为它总是从要查找的sg值开始,然后往前回溯,如果遇到已知sg值就可以返回,并保存所有算过的sg值。比起打表,要查找的sg值可能远远小于最后一个数,这样就不用打表一直求到最后一个数。

 

2

①打表sg函数值(320ms):

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>//#define Local_judge//HDU1536,SG函数,简单应用示例2using namespace std;int k,m,l;int number;int fib[110];int sg[10010];bool bj[10010];int res;void Get_sg(int t,int x){//x是sg数组上限,t是有多少种取石子的方法    memset(sg,0,sizeof(sg));    for(int i=1;i<=x;i++){        memset(bj,0,sizeof(bj));        for(int j=1;j<=t&&fib[j]<=i;j++){            bj[sg[i-fib[j]]]=1;        }        for(int j=0;j<=x;j++){            if(!bj[j]){                sg[i]=j;                break;            }        }    }}int main(){       #ifdef ONLINE_JUDGE//HDU上已经定义的宏    #else        freopen("in.txt","r",stdin);        freopen("out.txt","w",stdout);    #endif // Judge        /*    #ifdef Local_judge //类似的,可以自己定义,提交时注释掉最上面的define Local_judge        freopen("in.txt","r",stdin);        freopen("out.txt","w",stdout);    #endif // ACM    */    while(~scanf("%d",&k)&&k){        for(int i=1;i<=k;i++){            scanf("%d",&fib[i]);        }        sort(fib+1,fib+1+k);        scanf("%d",&m);        Get_sg(k,10000);</p><p>        //for(int i=0;i<=100;i++) cout<<sg[i]<<endl;</p><p>        for(int i=1;i<=m;i++){            scanf("%d",&l);            scanf("%d",&number);            res=sg[number];            for(int j=2;j<=l;j++){                scanf("%d",&number);                res^=sg[number];            }            if(res==0){                cout<<"L";            }            else{                cout<<"W";            }        }        cout<<endl;    }}

 


 ②回溯在线求sg函数值(280ms):

#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>///注意一个缺陷:Sg_huisu中每次都要bool visted[n];memset();如果最大sg函数值n大一些,估计这个方法就爆栈了。所以理解这种回溯在线查找sg值的思想即可。using namespace std;int k,m,num,number;int s[110];int sg[10010];//sg上限是石子个数而不是石子堆堆数int Sg_huisu(int x){//很多人把这一步叫做dfs,但是我认为称为回溯更恰当一些。因为dfs找到合适的就return了, 而这里每一层都要返回一个结点出发到达的每一个节点的情况,然后进行比较,再返回上一层。    if(sg[x]!=-1)        return sg[x];    bool visted[110];//visted[x],x上限是最大的sg函数值,你如果看不出来,可以打表所有sg值看看....    memset(visted,0,sizeof(visted));    for(int i=0;i<k;i++){        if(s[i]<=x){            Sg_huisu(x-s[i]);            visted[sg[x-s[i]]]=1;        }    }    for(int i=0;;i++){//同上一个注释,建立在visted数组内元素个数不超过最大sg值的基础上        if(!visted[i]){            sg[x]=i;            break;        }    }    return sg[x];}int main(){    while(~scanf("%d",&k)&&k){        for(int i=0;i<k;i++){            scanf("%d",&s[i]);        }        scanf("%d",&m);        sort(s,s+k);        memset(sg,-1,sizeof(sg));//针对分解后的子游戏,所以下文中几次询问或者每次询问时给出几个子游戏,对sg[]并没有影响。        while(m--){            scanf("%d",&num);            int ans=0;//0^0=0,0^1=1;            for(int j=0;j<num;j++){                scanf("%d",&number);                ans^=Sg_huisu(number);            }            if(ans==0){                cout<<"L";            }            else{                cout<<"W";            }        }        cout<<endl;    }}


 

 

0 0
原创粉丝点击