状压DP—— Military Class

F - Military Class

There is a military class of 2∗n soldiers, and the commander wants all of them to get partnered into n pairs. He divides the soldiers into two lines of length n, and numbers the soldiers in both lines from 1 to n.

The ith numbered soldiers in the first line can be partnered with the jth numbered soldiers in the second line if |i−j|≤e. However, there are k pairs of soldiers that cannot be paired together. You need to print the number of ways you can match the soldiers into n pairs such that the constraints above are met. One way is different than the other if at least one soldiers has a different partner.

Input
The first line contains 3 integers n,e,k(1≤n≤2000,0≤e≤4,0≤k≤2000), the number of soldiers in each line, the value that determines the range, and the number of invalid pairs, respectively.

Each of the next k lines contains two integers ui,vi(1≤ui,vi≤n), the number of the soldiers from the first line and the number of the soldiers from the second line that cannot be matched together respectively. No pair of soldiers will appear twice in the input.

Output
Output the number of ways modulo 1 0 9 + 7 10^9+7 109+7, on a single line.

题解:

我们的e范围很小最大就是2*e+1=9,所以可以考虑状压。dp[i][j]代表选择第i个人并且状态是j的方案数。这里用一维数组就可以了。所以第一层循环就是匹配到第几个了,第二层循环就是和9个人里面的谁匹配,然后枚举之前的匹配状态如果匹配过则跳过。最后把匹配到第n个人的所有状态方案数加起来就可以了。
解释一下为啥要右边移动,因为我们选最右边的时候在二进制的表示下滑到了最左边。所以我们在选人的时候二进制是反的。那么我们当前位置的状态用上一个位置的状态表示就是右边移动(因为平常我们在本子上写的时候上一个窗口是左移。)然后就可以用上一个状态来更新当前状态了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e3+7;
const int mod=1e9+7;
int dp[1<<10|10],tmp[1<<10|10];
bool g[N][N];
signed main()
{
    int state=(1<<9)-1;
    int n,e,m; cin>>n>>e>>m;
    for(int i=1;i<=m;i++) {
        int a,b; cin>>a>>b;
        g[a][b]=1;
    }
    dp[0]=1;
    for(int i=1;i<=n;i++){
        memset(tmp,0,sizeof tmp);
        for(int k=-e;k<=e;k++){
            int j=k+i;
            if(j<1||j>n) continue;
            if(g[i][j]) continue;
            for(int s=0;s<=state;s++){
                if((s>>1)&(1<<(k+4))) continue;
                tmp[(s>>1)|(1<<(k+4))]=(tmp[(s>>1)|(1<<(k+4))]+dp[s])%mod;
            }
        }
        memcpy(dp,tmp,sizeof tmp);
    }
    int res=0;
    for(int s=0;s<=state;s++){
        res=(res+dp[s])%mod;
    }
    cout<<res<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值