关闭

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

104人阅读 评论(0) 收藏 举报
分类:

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函数,简单应用示例2
using 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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:69665次
    • 积分:1929
    • 等级:
    • 排名:千里之外
    • 原创:181篇
    • 转载:2篇
    • 译文:0篇
    • 评论:5条
    最新评论
    Baidu statistics