zoukankan      html  css  js  c++  java
  • 【学习笔记】状压dp

    状压dp 学习日志 ---2019.1.21

    标签(空格分隔): 状压dp


    推荐blog:
    【暖*墟】

    状态压缩动态规划 状压DP

    神仙巨佬讲位运算

    lead into

    位运算

    为了更好的理解状压dp,首先介绍位运算相关的知识。

    1.x&y,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。例如3(11)&2(10)=2(10)。

    2.x|y,会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值。例如3(11)|2(10)=3(11)。

    3.xy,会将两个十进制数在二进制下进行异或运算,然后返回其十进制下的值。例如3(11)2(10)=1(01)。

    ’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充

    ’>>’是右移操作,x>>1相当于给x/2,去掉x二进制下的最右一位

    常见的应用:

    1.判断一个数字x二进制下第i位是不是等于1。
    方法:if(((1<<(i−1))&x)>0)

    将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。

    2.将一个数字x二进制下第i位更改成1。
    方法:x=x|(1<<(i−1))

    3.把一个数字二进制下最靠右的第一个1去掉。
    方法:x=x&(x−1)


    例题1
    玉米田Corn Fields

    /*【洛谷p1879】玉米田
    一块长方形的牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12)。
    从中选择一些作为草地,但不会选择两块相邻的土地。
    如果不考虑草地的总块数,一共有多少种选择方案?(0也是一种方案)。*/
    
    #include<bits/stdc++.h>
    #define mod 100000000    
    using namespace std;
    int n,m,tot,state[1500],dp[15][1500],ans,cur[15];
    
    //dp表示当前最大值,第一维是行数,第二维是状态数,cur是每行的情况,state是预存的可能状态
    
    /*这题也是用二进制来表示状态,先预处理:枚举一行内(不考虑地图因素)每一种状态,看看是否合法,合法的话就打个标记,以便后面操作.先处理第一行,枚举下面行的时候再枚举一遍上一行,看看两行放置是否合法,合法就累计上一行计数即可。*/
    
    inline int read()
    {
        int ans=0;    
        char last=' ',ch=getchar();
        while(ch<'0' || ch>'9') last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    
    inline bool fit(int x,int k)  
    {
        return !(state[x]&cur[k]);
    }
    
    inline void init()              
    {       
        for (int i=0;i<(1<<n);i++)
            if (!(i&(i<<1)))
                state[++tot]=i;
    }
    
    int main()
    {
        m=read();
        n=read();
        init();
        int k;
        for (int i=1;i<=m;i++)
            for (int j=1;j<=n;j++)
            {
                k=read();
                if (!k)
                    cur[i]+=(1<<(n-j));
            }
        for (int i=1;i<=tot;i++)             
            if (fit(i,1))
                dp[1][i]=1;
        for (int i=2;i<=m;i++)                
            for (int j=1;j<=tot;j++)      
            {
                if (!fit(j,i))
                    continue;
                for (k=1;k<=tot;k++)      
                {
                    if (!fit(k,i-1))
                        continue;
                    if (state[j]&state[k])
                        continue;
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;       
                }
            }
        for (int i=1;i<=tot;i++)
            ans=(ans+dp[m][i])%mod;
        printf("%d",ans);
        return 0;
    }
    

    例题2

    Hie With The Pie

    #include<bits/stdc++.h>
    using namespace std;
    
    /*t = statue - (1<<(i-1)) : 代表当前没有到达i店的statue的前一个状态。
    d (statue , i) == max( d( t , j ) + m( j, i ) ) : j 代表状态t已经到达的店,也是状态t当前停留的店,+m( j, i) 经过了
    这段距离 状态t 到达了状态statue,也就是 t +1<<(i-1) == statue;*/
    
    #define inf 0x3f3f3f3f
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    int d[1<<12][12];
    int m[12][12];
    int n;
    
    void Floyd()
    {
        for(int k=0; k<=n; k++)
            for(int i=0; i<=n; i++)
                for(int j=0; j<=n; j++)
                    m[i][j]=min(m[i][j],m[i][k]+m[k][j]);
    }
    
    int main()
    {    
        while(~scanf("%d",&n))
        {
            for(int i=0; i<=n; i++)
                for(int j=0; j<=n; j++)
                    scanf("%d",&m[i][j]);
            memset(d,inf,sizeof(d));
            Floyd();
            d[0][0]=0;
            rep(i,1,(1<<n)-1)
            
            for(int i=1; i<(1<<n); i++)
            {
                for(int j=1; j<=n; j++)
                {
                    if(i&(1<<(j-1)))
                    {
                        int t=i-(1<<(j-1));
                        if(t==0)
                        {
                            d[i][j]=m[0][j];
                            continue;
                        }
                        for(int k=1; k<=n; k++)
                        {
                            if(t&(1<<(k-1)))
                            {
                                d[i][j]=min(d[i][j],d[t][k]+m[k][j]);
                            }
                        }
                    }
                }
            }
            int ans=inf;
            for(int i=1; i<=n; i++)
                ans=min(ans,d[(1<<n)-1][i]+m[i][0]);
            printf("%d
    ",ans);
        }
    }
    

    例题3

    互不侵犯
    注:这是昨年坐过的题

    /*【洛谷p1896】互不侵犯
    国王能攻击到它上下左右、以及左上左下右上右下八个方向上一个格子。
    在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。*/
    
    #include<bits/stdc++.h>
    using namespace std;
    int n,k;
    long long dp[10][15000][80],king[15000],state[15000],tot,ans;        
    //dp[i][j][k]表示第i行选j状态在这一行及之前摆上k个国王的方案总数
    //state:状态,king:那一行的国王总数
    
    inline int read()                               
    {
        int x=0,f=1;
        char c=getchar();
        while (!isdigit(c))
            f=c=='-'?-1:1,c=getchar();
        while (isdigit(c))
            x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    
    inline void init()                               
    {
        tot=(1<<n)-1;
        for (int i=0;i<=tot;i++)
            if (!((i<<1)&i))                            
            {
                state[++ans]=i;
                int t=i;
                while (t)                            //记录国王个数
                    king[ans]+=t%2,t>>=1;            //注意是右移一位
            }
    }
    
    int main()
    {
        n=read(),k=read();
        init();
        for (int i=1;i<=ans;i++)                         
            if (king[i]<=k)
                dp[1][i][king[i]]=1;
        for (int i=2;i<=n;i++)                            //枚举行
            for (int j=1;j<=ans;j++)                        //枚举这行方案
                for (int p=1;p<=ans;p++)                //枚举上行方案
                {
                    if (state[j]&state[p])continue;
                    if (state[j]&(state[p]<<1))continue;
                    if (state[p]&(state[j]<<1))continue;
                    for (int s=1;s<=k;s++)                //枚举国王个数
                    {
                        if (king[j]+s>k)continue;
                        dp[i][j][king[j]+s]+=dp[i-1][p][s];
                    }
                }
        for (int i=1;i<=n;i++)
            for (int j=1;j<=ans;j++)
                tot+=dp[i][j][k];
        printf("%lld",tot);
        return 0;
    }
     
    
    

    例题4
    炮兵阵地

    待填坑


  • 相关阅读:
    Delphi中Android运行和JNI交互分析
    C++ 中内存分配和回收
    Delphi Android程序启动过程
    Delphi XE的RTTI增强,动态Hook某些内部事件
    Win7下超级管理员创建普通权限任务
    再探Delphi2010 Class的构造和析构顺序
    Delphi2010新发现-类的构造和析构函数功能
    【背包专题】01背包
    Delphi2010的RTTI增强
    用WebBrowser实现HTML界面的应用和交互 good
  • 原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11634729.html
Copyright © 2011-2022 走看看