• 【bzoj1087】【互不侵犯King】状压dp裸题(浅尝ACM-D)


    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=54329606
    向大(hei)佬(e)势力学(di)习(tou)

    Description

      在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
    左下右上右下八个方向上附近的各一个格子,共8个格子。

    Input

      只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

    Output

      方案数。

    Sample Input

    3 2
    Sample Output

    16

    看数据范围,1<=N<=9,什么东西的的数据范围这么小啊,总不可能是放水,所以想来也是状态压缩dp了

    说道状压dp,刚开始学习的时候打的是salesman,是一道用记忆化搜索的题,害我进了一个误区,竟以为状压都要用记忆化搜索。然而状压的实质只是将状态用二进制的数字表示,结合进各种dp中去。

    这类题的状态通常有优化,枚举的2^N的数字中有很大一部分是不合法的,如果每次都枚举完再判断的话有可能会超时,所以可以用一个state数组来储存合法状态(但要注意state[i]中的i不是二进制状态)

    另外灵活应用位运算来判断合法不合法也是一个重要的技巧
    目前我所总结到的:
    1、& 可以判断是否在同一列
    2、<< 和>> 可以挪动,与其他运算符结合使用可以判断斜方向上的东西
    3、| 并上两行的,通常用于三行的dp

    代码(有我的笨判断方法,好在对时间复杂的要求不高)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    
    const int N=15;
    
    struct Node{
        int state,cnt;
    }a[600];
    int sz=0;
    int n,K;
    ll dp[N][600][N*N];
    
    bool check(int state){
        bool bj=0;
        int now;
        while(state){
            now=(state&1);
            if(bj==0&&now==1) bj=1;
            else if(bj==1){
                if(now==1) return false;
                bj=0;
            }
            state>>=1;
        }
        return true;
        /*if(state&(state<<1)) return false;
        if(state&(state>>1)) return false;
        return true;*/
    }
    int lowbit(int x){
        return x&(-x);
    }
    int count(int state){
        int now,rt=0;
        while(state){
            rt++;
            state-=lowbit(state);
        }
        return rt;
    }
    bool check2(int a,int b){
        if((a&b)!=0) return false;
        if(check((a|b))==false) return false;
        /*if((a&(b<<1))!=0) return false;
        if((a&(b>>1))!=0) return false;*/
        return true;
    }
    int main(){
        scanf("%d%d",&n,&K);
        for(int i=0;i<(1<<n);i++){
            if(check(i)){
                a[sz].state=i;
                a[sz].cnt=count(i);
                sz++;
            } 
        }
        for(int i=0;i<sz;i++){
            dp[1][i][a[i].cnt]=1;
        }
        for(int i=2;i<=n;i++){
            for(int j=0;j<sz;j++){
                for(int k=0;k<=K;k++){//0个也是可能的 
                    for(int g=0;g<sz;g++){
                        if(check2(a[j].state,a[g].state)&&k-a[j].cnt>=0) dp[i][j][k]+=dp[i-1][g][k-a[j].cnt];
                    }
                }
            }
        }
        ll ans=0;
        for(int i=0;i<sz;i++){
            ans+=dp[n][i][K];
        }
        printf("%lld",ans);//long long 
        return 0;
    }

    总结:
    最近总死在long long 上,下手前要先思考一下可能会达到的数据范围

  • 相关阅读:
    JS控制的几种页面跳转方式和传值
    文件管理
    文件:文件和文件夹
    上传文件
    购物车的例子
    使用ajax登录格式
    ajax 另外两种返回类型(json xml)
    省级三级联动
    thinkPHP--SQL连贯操作
    thinkPHP-空操作
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763160.html
Copyright © 2020-2023  润新知