Gym - 102219 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 109+7, on a single line.


题解:
e<5,考虑状压DP,设dp[i][S]表示当前处理到第2行的第i个士兵,第一行中与之距离<=e的士兵的匹配情况,0表示还没有匹配到,1表示已经匹配到士兵了,考虑转移,枚举S中1的位置,那么对应i-1位置上的状态也可以推出来。再通过枚举高位为0/1转移即可

例如第i个士兵对应的状态为11010,从后往前枚举到了第2个士兵,那么先将当前位置0,我们就得到了11000,由于i-1位置上包含的是区间[i-1-e,i-1+e],i位置上包含的是区间[i-e,i+e],所以我们将得到的状态右移一位就得到了i-1位置上的后2*e位是1100,然后枚举高位位0/1,所以i位置上状态11010可以由i-1位置上状态11100或者01100,然后转移即可


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 2000+50;
const int MAXM = 2e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int n,e,p,mp[MAXN][MAXN]; LL dp[MAXN][(1<<10)+50];
inline void add(LL &x,LL y){ x=(x+y)%MOD; }
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
	scanf("%d%d%d",&n,&e,&p);
	for(int i=1,x,y;i<=p;i++) scanf("%d%d",&x,&y),mp[x][y]=1;
	for(int i=0;i<=e;i++) if(!mp[i][1]) dp[1][1<<i]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<(1<<2*e+1);j++){
			for(int k=0;k<2*e+1;k++){
				if(i+e-k>n || i+e-k<=0) continue;
				if(mp[i+e-k][i]) continue;
				if(j&(1<<k)){
					add(dp[i][j],dp[i-1][(j^(1<<k))>>1]);
					add(dp[i][j],dp[i-1][((j^(1<<k))>>1)|(1<<2*e)]);
				}
			}
		} 
	}
	int sta=(1<<2*e+1)-1;LL res=0;
	for(int i=0;i<e;i++) sta-=(1<<i);
	cout<<dp[n][sta]<<'\n';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值