Solution to a problem P1896 [[SCOI2005] non-aggression]

Topic Link

Solution [SCOI2005] nonaggression

Title effect: Given two numbers \ (n-\) , \ (K \) , find the \ (n ^ 2 \) disposed within the size of the board \ (K \) king of the number of programs

Analyze the topic: \ (the n-\) is very small ah, \ (1 \ the n-Leq \ Leq 9 \) , and the king's attack range is also very characteristic, konjac immediately thought of like pressure \ (dp \)

Little analysis can list \ (DP \) state to \ (f [i] [s ] [k] \) to indicate the front \ (I \) -th row, \ (I \) status line for \ (S \) , to the second \ (I \) lines a total of \ (K \) the total number of programs king

State want easy transfer equation it?

\ (F [I] [S] [K] = \ F SUM [I -. 1] [Z] [K - CNT (S)] \) , which we enumerated by the line state \ (Z \) metastases to, \ (CNT (X) \) represents the binary number (X \) \ inner (1 \) \ (i.e., the total number of state \ (S \) number king)

Finally, the border:

\ (f [1] [S] [cnt (S)] = 1 \) , this should be well understood it

Finally, in order to accelerate, we could start a possible pre-treatment status (such as single line next two king is certainly not)

The last offer konjac code:

Note: s is the number of code states

#include <cstdio>
using namespace std;
const int maxn = 10;
const int maxs = 1 << 10;//方案总数
inline int cnt(int x){//统计二进制下1的数量
    int ret = 0;
    while(x){
        if(x & 1)ret++;
        x >>= 1;
    }
    return ret;
}
inline bool check(int x){//判断方案x是否可行(用于预处理)
    if(x & (x << 1))return false;
    if(x & (x >> 1))return false;
    return true;
}
int status[maxs],tot;//预处理出的方案,tot为方案总数
long long f[maxn][maxs][maxn * maxn];//dp数组
int N,K,full;
int main(){
    scanf("%d %d",&N,&K);
    full = (1 << N) - 1;//full全集
    for(int i = 0;i <= full;i++)//预处理
        if(check(i))
            status[++tot] = i;
    for(int i = 1;i <= tot;i++)//边界条件
        f[1][i][cnt(status[i])] = 1;
    for(int i = 2;i <= N;i++)//dp
        for(int s = 1;s <= tot;s++)
            for(int k = cnt(status[s]);k <= K;k++)
                for(int z = 1;z <= tot;z++){//枚举由上一行的状态z转移而来
                    int x = status[s];
                    int y = status[z];
                    if(x & y)continue;//判断当前行状态x与枚举的上一行状态y是否冲突
                    if(x & (y >> 1))continue;
                    if(x & (y << 1))continue;
                    f[i][s][k] += f[i - 1][z][k - cnt(status[s])];
                }
    long long ans = 0;
    for(int i = 1;i <= tot;i++)//最后统计全都加起来
        ans += f[N][i][K];
    printf("%lld\n",ans);
    return 0;
}

Guess you like

Origin www.cnblogs.com/colazcy/p/11514721.html