zoukankan      html  css  js  c++  java
  • 洛谷 P1896 [SCOI2005]互不侵犯

    洛谷 P1896 [SCOI2005]互不侵犯

    题目链接:洛谷 P1896 [SCOI2005]互不侵犯

    算法标签: 动态规划(DP)状态压缩

    题目

    题目描述

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

    注:数据有加强(2018/4/25)

    输入格式

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

    输出格式

    所得的方案数

    输入输出样例

    输入 #1

    3 2
    

    输出 #1

    16
    

    题解:

    状压DP

    首先解释为什么是状压DP:

    • 根据题意我们能够得出是通过DP解决
    • 根据题中数据范围(1 le N le 9)

    其次,我们要考虑DP的几个属性:

    • 状态:

      1. 我们定义(f[i][j][s])为当前考虑前(i)行,第(i)行的国王的情况是编号为(j)的状态(压缩),在这(i)行共有(s)个国王的情况的总数。
      2. (sit[])(gs[])分别表示某一点的状态和此状态下这一行放的国王数量。这里需要有dfs预处理。
      void dfs(int sum, int tot, int now)
      {
      	if (now >= n)
      	{
      		sit[ ++ cnt] = sum;
      		gs[cnt] = tot;
      		return ;
      	}
      	dfs(sum, tot, now + 1);
      	dfs(sum + (1 << now), tot + 1, now + 2);
      }
      
    • 转移:

      1. 同一行国王的冲突:我们可以直接在预处理时候解决。
      2. 上下国王的冲突:if (sit[j] & sit[k]) continue ;​
      3. 左上方和右下方国王的冲突:if((sit[j] << 1) & sit[k]) continue ;
      4. 左下方和右上方国王的冲突:if (sit[j] & (sit[k] << 1)) continue ;
      5. 最终的转移过程按照直接处理每一行即可。
      for (int i = 2; i <= n; i ++ )
      {
      	for (int j = 1; j <= cnt; j ++ )
      	{
      		for (int k = 1; k <= cnt; k ++ )
      		{
      			if (sit[j] & sit[k])
      				continue ;
      			if ((sit[j] << 1) & sit[k])
      				continue ;
      			if (sit[j] & (sit[k] << 1))
      				continue ;
      			for (int l = gs[j]; l <= m; l ++ )
      				f[i][j][l] += f[i - 1][k][l - gs[j]];
      		}
      	}
      }
      

    AC代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 10;
    
    const int M = 110;
    
    const int NM = 2020;
    
    int n, m, sit[NM], gs[NM], cnt;
    long long f[N][NM][M];
    void dfs(int sum, int tot, int now)
    {
    	if (now >= n)
    	{
    		sit[ ++ cnt] = sum;
    		gs[cnt] = tot;
    		return ;
    	}
    	dfs(sum, tot, now + 1);
    	dfs(sum + (1 << now), tot + 1, now + 2);
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	dfs(0, 0, 0);
    	for (int i = 1; i <= cnt; i ++ )
    	{
    		f[1][i][gs[i]] = 1;
    	}
    	for (int i = 2; i <= n; i ++ )
    	{
    		for (int j = 1; j <= cnt; j ++ )
    		{
    			for (int k = 1; k <= cnt; k ++ )
    			{
    				if (sit[j] & sit[k])
    					continue ;
    				if ((sit[j] << 1) & sit[k])
    					continue ;
    				if (sit[j] & (sit[k] << 1))
    					continue ;
    				for (int l = gs[j]; l <= m; l ++ )
    					f[i][j][l] += f[i - 1][k][l - gs[j]];
    			}
    		}
    	}
    	long long ans = 0;
    	//printf("%d
    ", cnt);
    	for (int i = 1; i <= cnt; i ++ )
    		ans += f[n][i][m];
    	printf("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    LeetCode算法题-Sum of Two Integers(Java实现)
    LeetCode算法题-Valid Perfect Square(Java实现-四种解法)
    LeetCode算法题-Intersection of Two Arrays II(Java实现)
    LeetCode算法题-Intersection of Two Arrays(Java实现-四种解法)
    LeetCode算法题-Reverse Vowels of a String(Java实现-四种解法)
    LeetCode算法题-Reverse String(Java实现)
    LeetCode算法题-Power of Four(Java实现-六种解法)
    LeetCode算法题-Power Of Three(Java实现-七种解法)
    添加动态输出 Adding Dynamic Output 精通ASP-NET-MVC-5-弗瑞曼 Listing 2-7
    关于 退步编程 和 退步看书的思考
  • 原文地址:https://www.cnblogs.com/littleseven777/p/11841577.html
Copyright © 2011-2022 走看看