• 【状态压缩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

  • 相关阅读:
    CocosCreator 手动设置刚体接触回调函数
    CocosCreator 组件添加依赖的其它组件
    Cocos Creator 动画控制
    Cocos Creator Editor 扩展右键菜单
    CocosCreator 代码添加点击事件函数
    jsfl 读取xml
    Markdown 箭头
    Markdown 数学公式输入
    Cocos Creator Editor 创建.anim文件
    VS2017调试技巧
  • 原文地址:https://www.cnblogs.com/antiquality/p/9351132.html
Copyright © 2020-2023  润新知