P1896 [SCOI2005]互不侵犯(状压dp)

题目传送门

题意: 在一个n*n的棋盘上放k个国王,让他们互不攻击,问你有多少种放置的方案。(一个国王会攻击与之相邻的8个格子里的国王。)(1 <=n <=9, 0 <= k <= n * n)

思路: 我们可以先预处理出单独一行时,合法的放置情况。然后我们用 f [ i ] [ j ] [ q ] f[i][j][q] f[i][j][q]表示放完了前i行,第i行状态为j,总共放了q个的时候的方案数。i和q都好表示,但是我们需要考虑如何表示当前行状态j。考虑到一行最多9个,我们可以用二进制去表示,一个9位的二进制最大是512,其中除去不合法的情况,合法情况数量肯定是控制在 1 0 2 10^2 102级别的。然后我们考虑转移,先把第一行的所有合法情况置为1,然后从2开始枚举,当前行的状态j和上一行的状态p,再枚举当前总共放置q个国王。
f [ i ] [ j ] [ q ] + = ∑ f [ i − 1 ] [ p ] [ q − n u m ( j ) ] f[i][j][q] +=\sum f[i-1][p][q-num(j)] f[i][j][q]+=f[i1][p][qnum(j)],num(j)表示状态j中国王个数。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
    int x=0,f=1;
    char ch=gc();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=gc();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=gc();
    }
    return x*f;
}
using namespace std;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const double eps=1e-7;
int n,k;
vector<int>v;
void dfs(int x, int num, int cur)
{
    if (cur >= n)    // 有新的合法状态
    {
        v.pb(x);
        return ;
    }
    dfs(x, num, cur + 1);  // cur位置不放国王
    dfs(x + (1 << cur), num + 1,cur + 2);  // cur位置放国王,与它相邻的位置不能再放国王
}
int get(int x)
{
    int cnt=0;
    while(x)
    {
        if(x&1)
            cnt++;
        x>>=1;
    }
    return cnt;
}
int f[10][600][100];
int solve()
{
    cin>>n>>k;
    dfs(0,0,0);
    for(int i=0; i<v.size(); i++)
        f[1][i][get(v[i])] = 1;
    for(int i=2; i<=n; i++)
    {
        for(int x=0; x<v.size(); x++)
        {
            for(int y=0; y<v.size(); y++)
            {
                int j = v[x], p = v[y];
                if(j&p)
                    continue;
                if((j<<1)&p)
                    continue;
                if((j>>1)&p)
                    continue;
                for(int q=get(j); q<=k; q++)
                {
                    f[i][x][q] += f[i-1][y][q-get(j)];
                }
            }
        }
    }
    int res=0;
    for(int i=0; i<v.size(); i++)
        res += f[n][i][k];
    cout<<res<<endl;
    return 0;
}
signed main()
{
//    int _;
//    cin>>_;
//    while(_--)
    solve();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值