zoukankan      html  css  js  c++  java
  • BZOJ1087状压DP 解题报告

    1087: [SCOI2005]互不侵犯King

    Description

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

    Input

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

    Output

      方案数。

    Sample Input

    3 2

    Sample Output

    16

    状态压缩DP第一道题。作为第一道题。其实对于我这种动态规划都是自学的人来说。难度很大。但是。研读各大神犇的标程和解析后。开始慢慢的明白了一些,所以这里给一句忠告。没什么啃不下来的东西,如果啃不下来,那就多啃一会。

    说起动态规划。总是让我想起一系列问题。什么是状态,怎么转移,怎样决策。而这道题,好像没怎么用道决策。但是。什么是状态呢?怎么样在这道题实现转移。这是值得思考的。不思考是没有进步的。

    而这道题让我们一眼就可以想到DFS暴力枚举,以每个节点开始。枚举接下来每个点的位置。用二维数组来模拟位置的摆放。DFS递归的其实可以打表出来。而这个方法潜在的状态是上一个点的“位置”。每次枚举的是“位置”。

    这道题思考起来。关键:压缩。当发现是这种二维,而且DFS要炸的题。首先考虑压缩。降维?康托-压缩状态?。这里想到的是,以一层为一个状态元素。而我们知道这个状态有很多。因为最多就只有9*9,就9层。每一层有9个格子。那么我们可以提前预处理出来一层里面每一种正确的摆放方式,如果这种方式正确则打上标记。这里国王就只有摆或者不摆,很显然我们可以想到有可以用二进制来表示一层的状态,这样,一层就被我们压缩成一个数。一种可行性方案也被我们压成了一个数。

    接下来的就是对于每一层进行递推。我们都已经把每一种情况枚举出来。考虑每两层之间的关系,DP转移就成了枚举两层的可能组合性,之后就是方案数的转移。

    这里给出   F[i][q][j]   (一种状态)   其中这里的i代表层数,这里的j代表这一层如果是j这个状态。q代表着一层及其以上的所有放的国王数量。这里的j就指的是放的方式,举个栗子   j==85   那么那一层的方式就是 1010101(2)就是这样der。

    枚举每两层方式。F [i] [q+cnt_1[a]]  [a] += F[i-1] [q] [j];这个意思就是,如果上一层的摆放方式为J,这一层的摆放方式为a 那么这一层放q+(a方式的摆放国王的数量)的状态 是由 上一层摆放方式为j 摆放数量为q 的状态转移过来。(其中状态里存的是方案数)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int cnt[512],map_1[512][512],cnt_1[512];
    long long int f[10][512][512];
    int n,k,all;
    int first()
    {
        int s;
        for(int i=0;i<=all;i++)
    	if(((i>>1)&i)==0)//注释1
    	{
    	    cnt[i]=1;
    	    s=0;
    	    for(int j=i;j;j>>=1)s+=(j&1);
    	    cnt_1[i]=s;
    	}
        for(int i=0;i<=all;i++)if(cnt[i])
    	for(int j=0;j<=all;j++)if(cnt[j])
    	    if( ((i&j)==0) && (((i>>1)&j)==0) && (((j>>1)&i)==0))
    		map_1[i][j]=1;   //注释2
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        all=(1<<n)-1;
        first( );
        for(int i=0;i<=all;++i)if(cnt[i])f[1][cnt_1[i]][i]=1;
        for(int i=2;i<=n;++i)
        	for(int a=0;a<=all;++a)if(cnt[a])
    	    for(int j=0;j<=all;++j)if(cnt[j])
    		if(map_1[j][a])
    		     for(int q=cnt_1[j];q+cnt_1[a]<=k;++q)   //注释3
    		            f[i][q+cnt_1[a]][a]+=f[i-1][q][j];
        long long int ans=0;
        for(int i=0;i<=all;++i)ans+=f[n][k][i];
        printf("%lld",ans);
        return 0;
    }
    

    注释:

    1,这里作用是判断是否这种状态是不是正确。

    2,判断两种状态能不能出线在一起(上下层)

    3,这里枚举q,因为每一层结合上一层还有这一层摆放的方式有很多,所以要考虑全面。

    最重要的一条

    不开long long见祖宗

    不开long long见祖宗

    不开long long见祖宗

    不开long long见祖宗

    不开long long见祖宗

    不开long long见祖宗

  • 相关阅读:
    springboot配置视图控制器
    springboot测试的方法
    cookie之sameSite
    docker 安装Elasticsearch +kibana
    find + xargs 删除文件名中含有空格的文件
    git,composer 代理
    Composer更新依赖报错Fatal error解决方案
    compose 设置代理 SET HTTP_PROXY="http://192.168.1.103:8080"
    php使用composer常用问题及解决办法集:zlib_decode():data error......
    ERROR: .FileNotFoundError: [Errno 2] No such file or directory: '.\docker-compose.yml:docker-compose.apache.yml'
  • 原文地址:https://www.cnblogs.com/uncle-lu/p/5954976.html
Copyright © 2011-2022 走看看