洛谷P1896 [SCOI2005]互不侵犯

题链
dp维数不够?再加一维。还不够?再加再加!
当使用dp数组的时候,发现一个元素表示的情况仍然存在许多情况,需要再细分才能写出状态转移方程,那就再加一维试试。
做难题第一次这样一发入魂(ac),开心!

题目描述

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

注:数据有加强(2018/4/25)
输入格式

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

所得的方案数
输入输出样例
输入 #1

3 2

输出 #1

16

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=(1<<9)+10;
ll c[100];
ll dp[10][N][100];
ll countt[N];




void init_countt(int n){
int sm=(1<<n)-1;

for(int s=1;s<=sm;++s)
    for(int i=0;i<=n-1;++i){
      int i1=i+1;
      if(
         (i<n-1)
         &&
         (s&  (1<<i) )
         &&
          (s&  (1<<i1) )

        ){

            countt[s]=-1;
            break;

         }



      if(s&(1<<i))++countt[s];


    }

}



int main(){
int n,k;
cin>>n>>k;
int sm=(1<<n)-1;


init_countt(n);


ll ans=0;
for(int s=1;s<=sm;++s){
    if(countt[s]<0)continue;
    ++dp[0][s][countt[s]];
    ans+=dp[0][s][k];

}



for(int i=1;i<=n-1;++i){
    for(int s=0;s<=sm;++s){
        if(countt[s]<0)continue;

        for(int s0=0;s0<=sm;++s0){
            if(countt[s0]<0)continue;
            if(s&s0)        continue;
            if((s>>1)&s0)   continue;
            if((s<<1)&s0)   continue;

            for(int j=0;j<=k-countt[s];++j)
                 dp[i][s][j+countt[s]]+=dp[i-1][s0][j];



        }

        ans+=dp[i][s][k];

    }




}

cout<<ans<<endl;
return 0;
}

教训:
1.不能使用两次选取,会有重复计算
把满足的情况都选出来,再用组合数Cnm来选取K个位置,会有重复计算。假如位置1,2,3,5满足情况,位置1,2,3,6满足情况,要选3个。选取1,2,3的情况就重复计算了。

2.对于代码中
ans+=dp[i][s][k];
不能再加上比k多的情况。如果取了,再从中选取k个,那不论选的是哪k个,该情况都会再别的地方被运算一遍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值