zoukankan      html  css  js  c++  java
  • 状态压缩 周伟论文+代码+例题

    所有题解报告可以在周伟论文中看到 但是没有代码  下面是我自己的或者从网上搜集到的代码

    在n*n(n≤20)的方格棋盘上放置n 个车(可以攻击所在行、列),求使它们不
    能互相攻击的方案总数。
    仅供和我一样的菜鸟们参考

    #include <iostream>
    #include <cmath>
    using namespace std;
    __int64 a[1100000];
    int main()
    {
        __int64 n;
        while (cin >> n){
              memset(a, 0, sizeof(a));
              a[0] = 1;
              for (int i = 1; i <= 1<<n; i ++){//注意这里是1左移n位不是n<<1,显然这里是在枚举0000~1111的每一种状态
                  for (int j = i; j > 0; j -= (j&-j)){//首先注意这里是倒推,因为要由之前的状态推出现在的状态。
                      a[i] += a[i&~(j&-j)];  //这里的位运算处理甚是漂亮,它可以保证每一次都刚好取到i的子集 ,首先j&-j可以得出在i之前的每一种状态j的最低位1的位置k,然后取反可以保证只有第k个位置刚好为0,那么求与之后就在原来i的基础上去除了第k个1
                  //比如说当前i枚举到0111,那么j&-j = 0001,则~(j&-j) = 1110,那么i&1110 = 0110,0110就是0111的一个子集,随后去掉当前最低位k,j变成0110,以此反复运算,直到j=0000
                  }   
              }
              cout<<a[(1<<n)-1]<<endl;     
        } 
        return 0;   
    }

    另外的一个代码

    #include<stdio.h>
    #include<string.h>
    const int MAXN = 1 << 20 ;
    __int64 f[MAXN + 23] ;
    int main() {
        int N ;
        while( scanf("%d" , &N) != EOF) {
            memset( f , 0 , sizeof(f)) ;
            int i , tmp = (1 << N) - 1 ;
            f[0] = 1 ;
            for( i = 1 ; i <= tmp ; i ++) {
                int tt = i , x ;
                while( tt != 0 ) {
                    x = tt&(-tt) ;
                    f[i] += f[i ^ x] ;
                    tt ^= x ;
                }
            }
            printf("%I64d
    " , f[tmp] ) ;
        }
        return 0 ;
    }


    前一个状态压缩的升级版
    在n*n(n≤20)的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互相攻击的方案总数。
    和前面差别不大,主要是加了一个不可达点的限制,那么用二进制记录每一个不可达点然后还是按照以前的进行位运算,我写的代码不知道对不对,只核对了3以下的数据,希望路过神牛予以点评或修改

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<time.h>
    const int MAX_N = 1 << 8 ;
    __int64 f[MAX_N] ;
    int map[23][23] , a[23] ;
    int N ;
    void init() {
        int i , j ;
        srand( time ( NULL )) ;
        for( i = 1 ; i <= N ; i ++) {
            a[i] = 0 ;
            for( j = 1 ; j <= N ; j ++) {
                //scanf("%d" , &map[i][j] ) ;
                map[i][j] = rand() % 2 ;
                a[i] = a[i]*2 + map[i][j] ;
                printf("%d " , map[i][j] ) ;
            }
            printf("
    ") ;
        }
        for( i = 1 ; i <= N ; i ++)
            printf("%d
    " , a[i] ) ;
    }
    void solve() {
        int i , tmp = (1 << N) - 1 ;
        memset( f , 0 , sizeof( f )) ;
        f[0] = 1 ;
        for( i = 1 ; i <= tmp ; i ++ ) {
            int tt = i , j ;
            int one[23] , top = 0 ;
            while( tt != 0 ) {
                one[++top] = tt&(-tt) ;
                tt ^= one[top] ;
            }
            for( j = 1 ; j <= top ; j ++) {
                if( ( one[j] & a[top] ) == 0 )
                    f[i] += f[i^one[j]] ;
            }
        }
        printf("%I64d
    " , f[tmp] ) ;
    }
    int main() {
        while( scanf("%d" , &N) != EOF) {
            init() ;
            solve() ;
        }
        return 0 ;
    }


    另外一个代码      2个代码 一起   防止看不懂

    #include <iostream>
    #include <cmath>
    using namespace std;
    __int64 a[1100000];
    int is_allow[20];//用位记录所有不能放置棋子的位置
    int counter[21][21];
    /*void turn(int x, int n)//转换成二进制输出,属测试代码
    {
         int t = x;
         int num= 0;
         int xx[100];
         while (x){
               xx[num++] = x%2;
               x/=2;
         }    
         for (int i = num; i < n; i ++)cout<<0;
         for (int i = num-1; i >=0 ; i --)cout<<xx[i];
        
         cout<<endl;
    }*/
    void chang(int n)//将输入的地图转化到is_allow中
    {
         for (int i = 1; i <= n; i ++){
             int sum = 0;
             for (int j =n; j >= 1; j --){//转化成十进制存储到is_allow中
                 sum += counter[i][n-j+1]*(int)pow(2.0, n-j);
             }   
             is_allow[i] = sum;
         }        
    }
    int main()
    {
        __int64 n;
        while (cin >> n){
              memset(is_allow, 0, sizeof(is_allow));
              memset(counter, 0, sizeof(counter));
              int t;
              scanf("%d", &t);
              for (int i = 0; i < t; i ++){
                  int x, y;
                  scanf("%d%d", &x, &y);
                  counter[x][y] = 1;//记录不能放置棋子的位置
              }
              chang(n);//转化
              memset(a, 0, sizeof(a));
              a[0] = 1;
              for (int i = 1; i <= 1<<n; i ++){
                  int cc = 0, tt = i;
                  while (tt)tt&=(tt-1),cc ++;//cc记录当前放到了第几行
                  int tmps = i^is_allow[cc];//在i中去除不能放置的位置
                  for (int j = tmps; j > 0; j -= (j&-j)){
                      a[i] += a[i&~(j&-j)];
                  } 
              }
              printf("%I64d", a[(1<<n)-1]);  
        } 
        return 0;   
    }


      给出一个n*m 的棋盘(n、m≤80,n*m≤80),要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻。 求使得任意两个棋子不相邻的放置方案数。


    这个题目的状态压缩模型是比较隐蔽的。观察题目给出的规模,n、m≤80,这个规模要想用 SC 是困难的,若同样用上例的状态表示方法(放则为 1,不放为0),2^80  无论在时间还是在空间上都无法承受。然而我们还看到 n*m≤80,这种给出数据规模的方法是不多见的,有什么玄机呢?能把状态数控制在可以承受的范围吗?稍微一思考,我们可以发现:9*9=81>80,即如果n,m 都大于等于 9,将不再满足n*m≤80 这一条件。所以,我们有n 或m 小于等于 8,而2^8 是可以承受的。我们假设 m≤n(否则交换,由对称性知结果不变)n  是行数 m  是列数,则每行的状态可以用 m 位的二进制数表示。但是本题和例 1 又有不同:例 1 每行每列都只能放置一个棋子,而本题却只限制每行每列的棋子不相邻。但是,上例中枚举当前行的放置方案的做法依然可行。我们用数组 s[1..num] 保存一行中所有的num 个放置方案,则s 数组可以在预处理过程中用DFS 求出,同时用c[i]保存第i 个状态中 1 的个数以避免重复计算。开始设计状态。如注释一所说,维数需要增加,原因在于并不是每一行只放一个棋子,也不是每一行都要求有棋子,原先的表示方法已经无法完整表达一个状态。我们用 f[i][j][k]表示第 i 行的状态为s[j]且前i 行已经放置了k 个棋子(2) 的方案数。沿用枚举当前行方案的做法,只要当前行的方案和上一行的方案不冲突即可,“微观”地讲,即s[snum[i]]和s[snum[i-1]]没有同为 1 的位,其中snum[x]表示第x 行的状态的编号。然而,虽然我们枚举了第 i 行的放置方案,但却不知道其上一行(i-1)的方案。为了解决这个问题,我们不得不连第i-1 的状态一起枚举,则可以写出递推式:

                         f[0][1][0]=1;
                         f[i][j][k]=∑f[i-1][p][k-c[j]]


    其中s[1]=0,即在当前行不放置棋子;j和p是需要枚举的两个状态编号,且要求s[j]与s[p]不冲突,即s[j]&s[p]=0。(3) 当然,实现上仍有少许优化空间,例如第i行只和第i-1行有关,可以用滚动数组节省空间。

    #include<stdio.h>
    #include<string.h>
    int s[263] , c[263] , top  ;
    int f[10][36][10] ;
    int N , M , pn ;
    // 产生一行里所有的合法状态
    void DFS( int t , int state , int count , int* flag) {
        if( t == M )
        {
            s[++top] = state ;
            c[top] = count ;
            return ;
        }
        flag[t + 1] = 0 ;
        DFS( t + 1 , state*2 , count , flag ) ;
        if( flag[t] == 0 ) 
        {
            flag[t + 1] = 1 ;
            DFS( t + 1 , state*2 + 1 , count + 1 , flag) ;
        }
    }
    void solve() {
        memset( f , 0 , sizeof( f )) ;
        f[0][1][0] = 1 ;
        int i , j , k , p ;
        for( i = 1 ; i <= N ; i ++) {
            for( j = 1 ; j <= top ; j ++) {
                for( p = 1 ; p <= top ; p ++) {
                    for( k = c[j] ; k <= pn ; k ++) {
                        if( (s[j]&s[p]) == 0 ) {
                            f[i][j][k] += f[i - 1][p][k - c[j]] ;
                        }
                    }
                }
            }
        }
        int sum = 0 ;
        for( i = 1 ; i <= top ; i ++) {
            sum += f[N][i][pn] ;
        }
        printf("%d
    " , sum ) ;
    }
    int main() {
        while( scanf("%d %d %d" , &N , &M , &pn ) != EOF) {
            int  flag[23] = { 0 };
            top = 0 ;
            if( N < M ) {
                N ^= M ;
                M ^= N ;
                N ^= M ;
            }
            printf("%d %d
    " , N , M ) ;
            DFS( 0 , 0 , 0 , flag) ;
            solve() ;
        }
        return 0 ;
    }



    内容参考于zhang360896270

    yang_7_46

    结合例题  SGU 223 

    【例3

    n*n(n≤10)的棋盘上放k个国王(可攻击相邻的8个格子),求使它们无法互相攻击的方案数

    http://acm.sgu.ru/problem.php?contest=0&problem=223

    思路看周伟的论文    代码献上

    #include<stdio.h>
    #include<string.h>
    int n,pn;
    int s[1<<11],c[1<<11],top;
    long long f[11][1<<11][111];
    
    void DFS(int t,int state,int count,int flag[])
    {
        if(t==n)
        {
            s[++top]=state;
            c[top]=count;
            return;
        }
        flag[t]=0;
        DFS(t+1,state*2,count,flag);
        if(t==0||flag[t-1]==0)
        {
            flag[t]=1;
            DFS(t+1,state*2+1,count+1,flag);
        }
    }
    /* 下面这个也对
    void dfs(int p,int last,int now,int cnt)
    {
        if (p == n){s[++top]=now;c[top]=cnt;return ;}
        dfs(p+1,0,now*2,cnt);
        if (!last) dfs(p+1,1,now*2+1,cnt+1);
    }*/
    void solve()
    {
        int i;
        memset(f,0,sizeof(f));
         for(i=1;i<=top;i++)
        {
            f[1][i][c[i]]=1;
        }
        for(i=2;i<=n;i++)
        //f[0][1][0]=1;这种方法一样
       // for (i=1;i<=n;++i)
        {
            for(int si=1;si<=top;si++)
            {
                for(int pi=1;pi<=top;pi++)
                {
                    for(int cnt=c[si];cnt<=pn;cnt++)
                    {
                        if((s[si]&s[pi])||(s[si]&(s[pi]>>1))||(s[si]&s[pi]<<1))
                           continue;
                        if(cnt-c[si]>=c[pi])
                         f[i][si][cnt]+=f[i-1][pi][cnt-c[si]];
                    }
                }
    
            }
        }
        long long sum=0;
        for(i=1;i<=top;i++)
        {
            sum+=f[n][i][pn];
        }
        printf("%lld
    ",sum);
    
    }
    int main()
    {
        int  flag[11];
        while(scanf("%d %d",&n,&pn)!=EOF)
        {
            top=0;
            memset(flag,0,sizeof(flag));
            DFS(0,0,0,flag);
           //dfs(0,0,0,0);
            solve();
        }
        return 0;
    }
    


     


     

    未完待续 hnust_xiehonghao

  • 相关阅读:
    OLAP ODS项目的总结 平台选型,架构确定
    ORACLE ORA12520
    ORACLE管道函数
    ORACLE RAC JDBC 配置
    ORACLE RAC OCFS连接产生的错误
    ORACLE 启动和关闭详解
    OLAP ODS项目的总结 起步阶段
    ORACLE RAC 配置更改IP
    ORACLE RAC OCR cann't Access
    ORACLE RAC Debug 之路 CRS0184错误与CRS初始化
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3231120.html
Copyright © 2011-2022 走看看