您的位置:首页 > 编程语言 > C语言/C++

51nod 1327 棋盘游戏

2017-06-18 16:40 127 查看
有一个N行M列的棋盘,即该棋盘被分为N*M格。现在向棋盘中放棋子,每个格子中最多放一个棋子,也可以一个不放。放完棋子后需要满足如下要求:
1)对于第i行来说,其从左往右的前left[i] 个格子(即最左侧的left[i] 个连续的格子)中恰好一共有1个棋子;
2)对于第i行来说,其从右往左的前right[i]个格子(即最右侧的right[i]个连续的格子)中恰好一共有1个棋子;
3)对于每一列来说,这一列上的所有格子内含有的棋子数不得超过1个。
其中,1)与2)条件中,对所有 i 满足 left[i]+right[i] <= M,即两个区间不会相交。
问,符合上述条件情况下棋子的不同放法一共有多少种?输出放法的个数 mod  1,000,000,007后的结果。

例如样例中,只有如下图一种方法。
           


Input
第一行包含两个整数,N,M,其中1<=N<=50,2<=M<=200.
之后有N行,每行两个数left[i],right[i],其中,1<=left[i],right[i]<=M,且 left[i]+right[i] <= M。


Output
一个整数,即符合条件的方案数mod 1,000,000,007后的结果。


Input示例
2 4
1 2
2 1


Output示例
1


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

神奇DP+思路~

用f[i][j][k]表示当前i列,一共放了j枚棋子,有k行的右区间不满足。

不需要把a数组排序,排了也没有用的哼。

我们显然不能同时DP左右区间,所以我们只能在更新的时候强制满足左区间,DP右区间。

因为每一列最多只能填1个,所以我们可以枚举我们把这个棋子填在什么位置。

对于每一列i,我们预处理出l为左区间的右边界==i的行数,r为右区间的左边界==i的行数,block为i既不在左区间也不在右区间的行数。

那么就有四种放置情况:满足一个左区间,满足一个右区间,放在一个中间区域(即一个block),以及不放。

满足左区间时,我们从已有左区间中选择一个,然后强制假设之前的j枚棋子中有左区间-1个刚好满足了左区间,其余同理。

我枚举区间的时候i是从0开始的导致代码很难看,i+1表示的都是真正的i的位置!

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define mod 1000000007
#define ll long long

int n,m,f[202][202][52],c[202][202],mi[202],ans,l,r,block;

struct node{
int l,r;
}a[51];

int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}

void add(int &a,int b)
{
a+=b;
if(a>=mod) a-=mod;
}

int main()
{
n=read();m=read();
for(int i=1;i<=n;i++) a[i].l=read(),a[i].r=m-read()+1;
for(int i=0;i<=201;i++) c[i][0]=1;
for(int i=1;i<=201;i++)
for(int j=1;j<=i;j++) add(c[i][j],c[i-1][j]+c[i-1][j-1]);
mi[0]=1;
for(int i=1;i<=201;i++) mi[i]=(ll)mi[i-1]*i%mod;
for(int i=1;i<=201;i++)
for(int j=1;j<=i;j++) c[i][j]=(ll)c[i][j]*mi[j]%mod;
f[0][0][0]=1;
for(int i=0;i<m;i++)
{
l=r=block=0;
for(int j=1;j<=n;j++) l+=(a[j].l==i+1),r+=(a[j].r==i+1),block+=(a[j].l<i+1 && a[j].r>i+1);
for(int j=l-1;j<=i+1;j++)
for(int k=0;k<=n-r;k++)
{
if(j+1>=l && l>=1) add(f[i+1][j+1-l][k+r],(ll)c[l][1]*c[j][l-1]%mod*f[i][j][k]%mod);
if(j>=l)
{
add(f[i+1][j+1-l][k+r],(ll)c[j][l]*f[i][j][k]%mod);
if(k+r>=1) add(f[i+1][j-l][k+r-1],(ll)c[j][l]*f[i][j][k]%mod*c[k+r][1]%mod);
if(block) add(f[i+1][j-l][k+r],(ll)c[j][l]*f[i][j][k]%mod*c[block][1]%mod);
}
}
}
for(int i=0;i<=m;i++) add(ans,f[m][i][0]);
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 思路 DP