51nod 1327 棋盘游戏

来源:互联网 发布:淘宝人群标签在哪里看 编辑:程序博客网 时间:2024/06/05 12:39

有一个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 41 22 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 longint 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;}


原创粉丝点击