【51nod1327】棋盘游戏

题意

n×m的棋盘,放棋子,要求第i行左边 l i l_i li个格子和右边 r i r_i ri个格子各恰好有一个棋子,且每一列最多只能有1个棋子,问方案数,对 1 0 9 + 7 10^9+7 109+7取模
n ≤ 50 , m ≤ 200 n\leq50,m\leq200 n50,m200

Solution

这是一道DP好题。
太菜了以至于我想了半天连状态都没想到。
考虑到每一列最多放一个,那么考虑在列上做文章,设 f [ i ] [ ] f[i][] f[i][]表示做到了第i列……
然后怎么办呢,对于一列,假如一些行左区间到此为止了,那么就一定要将这些行放完,将这些行安排之前没有被放过的列;否则先把它们咕咕了。如果一些行的右区间从这一列开始,那么没有处理完的行就会多出几个。其次你也可以选择填在中间没有覆盖的地方。
如果这么考虑的话,状态就很好想了(可是就是没有这么考虑)
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示做到了第i列,在之前有j列没有被放过棋子,有k行已经到了右区间。
l i , r i , m i d i l_i,r_i,mid_i li,ri,midi分别为左区间右端点为i、右区间左端点为i、i列没有区间覆盖住的行数
每一列的转移要添加 r i + 1 r_{i+1} ri+1的右区间放置机会,要将 l [ i + 1 ] l[i+1] l[i+1]全部搞定。
初状态为 f [ 0 ] [ 0 ] [ 0 ] = 1 f[0][0][0]=1 f[0][0][0]=1
考虑三种转移:

  1. 不放在中间空着的,也不放在右区间,那么就要将 l i + 1 l_{i+1} li+1行安排前面没放棋子的那j列 f [ i + 1 ] [ j + 1 − l i + 1 ] [ k + r i + 1 ] + = f [ i ] [ j ] [ k ] ∗ A j + 1 l i + 1 f[i+1][j+1-l_{i+1}][k+r_{i+1}]+=f[i][j][k]*A_{j+1}^{l_i+1} f[i+1][j+1li+1][k+ri+1]+=f[i][j][k]Aj+1li+1
  2. 放在右区间 f [ i + 1 ] [ j − l i + 1 ] [ k + r i + 1 − 1 ] + = f [ i ] [ j ] [ k ] ∗ A j l i + 1 ∗ ( k + r i + 1 ) f[i+1][j-l_{i+1}][k+r_{i+1}-1]+=f[i][j][k]*A_j^{l_{i+1}}*(k+r_{i+1}) f[i+1][jli+1][k+ri+11]+=f[i][j][k]Ajli+1(k+ri+1)
  3. 放在中间空着的位置 f [ i + 1 ] [ j − l i + 1 ] [ k + r i + 1 ] + = f [ i ] [ j ] [ k ] ∗ A j l i + 1 ∗ m i d i + 1 f[i+1][j-l_{i+1}][k+r_{i+1}]+=f[i][j][k]*A_j^{l_{i+1}}*mid_{i+1} f[i+1][jli+1][k+ri+1]+=f[i][j][k]Ajli+1midi+1
#include
#include
#include
#include
#define fo(i,a,b) for(int i(a),_E_(b);i<=_E_;++i)
#define REP(i,a,b) for(int i(a),_E_(b);i<_E_;++i)
#define fd(i,a,b) for(int i(a),_E_(b);i>=_E_;--i)
#define ll long long
#define mo 1000000007
#define N 52
#define M 210

using namespace std;

int f[M][M][N],l[M],r[M],mid[M],n,m,p[M][M];

int main(){
	scanf("%d %d",&n,&m);
	fo(i,0,m){
		p[i][0]=1;fo(j,1,i)p[i][j]=1ll*p[i][j-1]*(i-j+1)%mo;
	}
	fo(i,1,n){
		int L,R;scanf("%d %d",&L,&R);
		++l[L];++r[m-R+1];++mid[L+1];--mid[m-R+1];
	}
	fo(i,2,m)mid[i]+=mid[i-1];
	f[0][0][0]=1;
	REP(i,0,m)fo(j,0,i)fo(k,0,n)if(f[i][j][k]){
		if(j+1>=l[i+1])(f[i+1][j+1-l[i+1]][k+r[i+1]]+=1ll*p[j+1][l[i+1]]*f[i][j][k]%mo)%=mo;
		if(j>=l[i+1]&&k+r[i+1])(f[i+1][j-l[i+1]][k-1+r[i+1]]+=1ll*p[j][l[i+1]]*(k+r[i+1])%mo*f[i][j][k]%mo)%=mo;
		if(j>=l[i+1])(f[i+1][j-l[i+1]][k+r[i+1]]+=1ll*p[j][l[i+1]]*mid[i+1]%mo*f[i][j][k]%mo)%=mo;
	}int ans=0;fo(i,0,m)ans=(ans+f[m][i][0])%mo;
	printf("%d",ans);
	return 0;
}

你可能感兴趣的:(DP)