【题解】互不侵犯 SCOI 2005 BZOJ 1087 插头dp

以前没学插头dp的时候觉得这题贼难,根本不会做,学了才发现原来是一裸题。

用二进制表示以前的格子的状态,0表示没放国王,1表示放了国王。

假设当前位置为(x,y),需要记录的是(x-1,y-1)至(x,y-1)的信息,共n+1个点。

每个状态有两种决策,第一种是这个格子不放国王,直接转移。

第二种是这个格子放国王,需要满足几个条件才能进行这步转移,条件很显然,具体见代码和注释。

因为状态数很少,所以也用不到BFS转移状态和Hash的技巧,所以这题还是很清真的,细节不多,码量也很小。

#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;
typedef long long ll;
const int MAXN = 9;

int n, k;

inline int getd( int x, int d ) { // 位运算相关
    return (x>>d)&1;
}
inline int setd( int x, int d, int v ) {
    return (x&(~(1<<d)))|(v<<d);
}

ll f[2][81][1024]; // 滚动数组
void update_no( int cur, int kk, int S ) { // 不放国王
    int nS = setd(S,n,0)<<1;
    f[cur^1][kk][nS] += f[cur][kk][S];
}
void update_yes( int cur, int kk, int S ) { // 放国王
    int nS = (setd(S,n,0)<<1)|1;
    f[cur^1][kk+1][nS] += f[cur][kk][S];
}
void solve() {
    int cur = 0;
    f[cur][0][0] = 1;
    for( int i = 0; i < n; ++i )
        for( int j = 0; j < n; ++j ) {
            memset( f[cur^1], 0, sizeof(f[cur^1]) );
            for( int kk = 0; kk <= k; ++kk )
                for( int S = 0; S < 1024; ++S )
                    if( f[cur][kk][S] ) {
                        update_no(cur,kk,S); // 任何格子都可以不放
                        if( ( !j || !getd(S,0) ) && // 在第一列或者左边没有国王
                            ( !i || !getd(S,n-1) ) && // 在第一行或者上面没有国王
                            ( !j || !i || !getd(S,n) ) && // 在第一行或者在第一列或者左上角没有国王
                            ( j == n-1 || !i || !getd(S,n-2) ) && // 在最后一列或者在第一行或者右上角没有国王
                            kk < k ) // 还有剩余的国王可以放
                            update_yes(cur,kk,S);
                    }
            cur ^= 1;
        }
    ll ans = 0;
    for( int S = 0; S < 1024; ++S ) ans += f[cur][k][S];
    printf( "%lld\n", ans );
}

int main() {
    scanf( "%d%d", &n, &k ), solve();
    return 0;
}
时间: 2024-08-29 04:23:22

【题解】互不侵犯 SCOI 2005 BZOJ 1087 插头dp的相关文章

互不侵犯King(bzoj 1087)

Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) Output 方案数. Sample Input 3 2 Sample Output 16 /* 一看就是状压DP,而且状态也很好设,dp[i][j][s]表示前i行填了j个国王,第i行的状态是s的方案数. 但

[BZOJ1087][SCOI2005]互不侵犯King解题报告|状压DP

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 好像若干月前非常Naive地去写过DFS... 然后其实作为状压DP是一道非常好的题啦>< 感觉直接无脑搞时间是下不来的 做了好几道预处理 使得最后DP的过程中没有任何一条转移是无用的 1 program bzoj1087; 2 var i,x,n,k,j,p,q,t1,t2:longint; 3 ans:int64; 4 a:array[-1

【题解】[SCOI2005] 互不侵犯 (状压DP)

[SCOI2005] 互不侵犯 终于懂一点状压DP了… 用一个数的二进制形式表示一整行的状态,比如 18(1010)表示第一列和第三列有国王. 然后用&判断是否可行: if((x&y)||((x<<1)&y)||(x&(y<<1))) continue;1code: #include<iostream>#include<cstdio>#include<cstring>#include<algorithm>

【BZOJ1187】[HNOI2007]神奇游乐园 插头DP

[BZOJ1187][HNOI2007]神奇游乐园 Description 经历了一段艰辛的旅程后,主人公小P乘坐飞艇返回.在返回的途中,小P发现在漫无边际的沙漠中,有一块狭长的绿地特别显眼.往下仔细一看,才发现这是一个游乐场,专为旅途中疲惫的人设计.娱乐场可以看成是一块大小为n×m的区域,且这个n×m的区域被分成n×m个小格子,每个小格子中就有一个娱乐项目.然而,小P并不喜欢其中的所有娱乐项目,于是,他给每个项目一个满意度.满意度为正时表示小P喜欢这个项目,值越大表示越喜欢.为负时表示他不喜欢

【状压dp】【bzoj 1087】【SCOI 2005】互不侵犯King

1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec Memory Limit: 162 MB Submit: 1991 Solved: 1185 Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) Output 方案数. Sa

BZOJ 1087 互不侵犯King (位运算)

题解:首先,这道题可以用位运算来表示每一行的状态,同八皇后的搜索方法,然后对于限制条件不相互攻击,则只需将新加入的一行左右移动与上一行相&,若是0则互不攻击,方案可行.对于每种方案,则用递推来统计,将前一排所有可以的情况全部加上即可.bit数组记录每个数字二进制位中1的个数,方便计算. if(check(j,q))f[i][j][k]+=f[i-1][q][k-bit[j]]; #include <iostream> #define rep(i,n) for (int i=0;i<

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------

【BZOJ】【1087】【SCOI2005】互不侵犯King

状压DP 我写的太水了……64ms才过,估计还有更好的做法,希望各位神犇不吝赐教>_<. 嗯这题很明显每一行都可以用一个2进制数表示放置方式的,(1表示放,0表示不放).然后预处理一下所有合法状态(同一行内的国王之间不会互相攻击),然后记f[i][j][k]为第i行,用第j种合法放置方式放国王,总共放了k个国王的方案数,转移的时候枚举上一行的状态,看是否和这一行的冲突(和预处理一样可以用位运算加速),然后累加即可,很基础的状压DP. WA了一次的原因:最后答案可能会爆int,必须用longlo

BZOJ 1087 互不侵犯king

       这道题与皇后问题极像,只是两者的攻击范围不一样,同时根据题目限制可以发现,这道题数据的特殊性,棋盘很小,因此想到用状态压缩DP的方法求解.        首先将每一行互不侵犯的可能列出来,用1.0的方式记录,之后根据要求会发现,每一行的情况受上一行的情况限制,于是从第一行进行一层层的判断.又由于国王的攻击是一个九宫格,因此难点在于两国王处于对角,则进行判断时将下一行向左移一位或向右移一位再进行判断.最后记得国王数是一定的,用一个变量记录一下.        程序无太大的突出,但每一