zoukankan      html  css  js  c++  java
  • 【状态压缩dp】bzoj1087: [SCOI2005]互不侵犯King

    状态压缩dp经典

    Description

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

    Input

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

    Output

      方案数。

    Sample Input

    3 2

    Sample Output

    16

    题目分析

    第一眼看上去是爆搜题?(不过应该会TLE)

    这里每一行的放置显然只和上一行有关系,自然考虑将状态压缩扔进dp状态里。

    用$f[i][j][k]$表示前$i$行放置了$j$个国王,第$i$行状态是$k$的方案数。

    有一个技巧,在处理出合法的状态之后,$O(statuses^2)$地处理这个状态下一行能够放的状态。

    转移方程不难想到,不过要注意的是仍然是拓扑序的问题。dfs下去显然是不行的,于是可以先枚举层数,再枚举当前层状态,最后枚举当前层安排的国王总数。这样被动转移下去就好了。

     1 #include<bits/stdc++.h>
     2 const int maxn = 1305;
     3 const int maxm = 1005;
     4 
     5 int n,k,mx;
     6 long long ans;
     7 long long f[13][203][maxn];
     8 int edgeTot,head[maxn],nxt[maxm],edges[maxm];
     9 int num[maxn],per[maxn];
    10 bool vis[maxn];
    11 
    12 int read()
    13 {
    14     char ch = getchar();
    15     int num = 0;
    16     bool fl = 0;
    17     for (; !isdigit(ch); ch = getchar())
    18         if (ch=='-') fl = 1;
    19     for (; isdigit(ch); ch = getchar())
    20         num = (num<<1)+(num<<3)+ch-48;
    21     if (fl) num = -num;
    22     return num;
    23 }
    24 void addedge(int u, int v)
    25 {
    26     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
    27 }
    28 int legal(int x)
    29 {
    30     int cnt = 0;
    31     for (int pre=0; x; x>>=1)
    32     {
    33         int t = x&1;
    34         if (pre&t) return 0;
    35         cnt += (pre = t)?1:0;
    36     }
    37     return cnt;
    38 }
    39 void init()
    40 {
    41     per[++per[0]] = 0;
    42     for (int i=1; i<mx; i++)
    43     {
    44         num[i] = legal(i);
    45         if (num[i])
    46             vis[i] = 1, per[++per[0]] = i;
    47     }
    48     addedge(0, 0);
    49     for (int i=1; i<=per[0]; i++)
    50     {
    51         for (int j=i+1; j<=per[0]; j++)
    52         {
    53             int x = per[i], y = per[j];
    54             if ((x&y)||(x&(y<<1))||(x&(y>>1))) continue;
    55             addedge(x, y), addedge(y, x);
    56         }
    57     }
    58 }
    59 int main()
    60 {
    61     memset(head, -1, sizeof head);
    62     n = read(), k = read(), mx = 1<<n;
    63     init();
    64     f[0][0][0] = 1;
    65 //    dp(0, 0, 0);        记忆化搜索的形式应该是不行的
    66     for (int i=0; i<n; i++)
    67         for (int t=(i?per[0]:1); t; t--)
    68         {
    69             int u = per[t];
    70             for (int p=head[u]; p!=-1; p=nxt[p])
    71             {
    72                 int v = edges[p];
    73                 for (int q=num[u]; q<=k-num[v]; q++)
    74                     f[i+1][q+num[v]][v] += f[i][q][u];
    75             }
    76         }
    77     for (int i=1; i<=per[0]; i++)
    78         ans += f[n][k][per[i]];
    79     printf("%lld
    ",ans);
    80     return 0;
    81 }

    END

  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/antiquality/p/9351132.html
Copyright © 2011-2022 走看看