题目大意:有N个人要坐在N个座位上,并且第i个人不能坐在第i个座位满足条件:这个座位两边都有座位并且都有人坐下并且两边座位不是同一种,当某人没有座位可坐时会离开,问使N人坐下共有多少种方法,结果对1e-9+7取模.
思路:区间dp,状态空间不难想出--dp[i][j]从第i个人到第j个人全部坐下共有多少种方法.由于dp[1][N]时边上两个座位是一定可以坐的,所以子状态下边上边上两个座位也可以直接坐.
这里我推荐一位大佬的解题报告,我是看了他的解题报告后才知道我开始列的状态转移方程为什么错了。https://blog.csdn.net/u013611908/article/details/43193651
状态转移方程为:假设最后剩下第k个座位,如果k是两边的座位,dp[i][j]=dp[i+1][k]+dp[i][k-1]
k是中间的座位:dp[i][j]=dp[i][j]+dp[i][k-1]*dp[k+1][j]*c[j-i][k-i]( i<k<j 并且第j-1个座位与第j+1个不同).
画图来解释为什么乘c[j-i][k-i],如图,假设最后一个人坐在第4个座位上,首先左边坐满有dp[1][3]种方法,右边坐满有dp[5][11]种.dp[1][3]*dp[5][11] (记为m) 是坐满1-3和5-11座位的所有方法总数,但是这不是dp[1][11],因为m只考虑到先坐满左边然后再坐满右边,或者相反.显然是可以第i个人坐左边,第i+1个人坐右边的.
考虑m种的一种特殊情况:
第4个座位最后坐,两边字母的下标表示坐的顺序(我举的例子可能是错的,但不影响),现在问题就是知道两边坐的顺序,问这种情况下11个人坐满有多少种方法,设为num.
如图是一种坐满方法(剩下是依次填入7,9,6,10,8,11,5)
可以得到一种坐满的方案:先坐第7个座位,然后是第9个,3,6,1,10,8,11,5,2,4
可以知道num=c[10][3],也就是说将两组相对有序的一列数合并成一组有序的数还要乘上组合数c[总数][一组数的总数];
实现如下:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 102
#define mod 1000000007
long long dp[N][N],c[N][N];
int a[N];
int main() {
int n;
int r,i,j,k;
for( i=1;i<=110;i++ )
{
c[i][0]=c[i][i]=1;
for( j=1;j<i;j++ )
c[i][j]=( c[i-1][j-1]+c[i-1][j] )%mod;
}
while( scanf("%d",&n)!=-1 ){
memset(dp,0,sizeof(dp));
for( i=1;i<=n;i++ )
scanf("%d",&a[i]);
a[0]=a[2];
a[n+1]=a[n-1];
for( i=1;i<=n;i++ )
dp[i][i]=1;
for( i=1;i<=n-1;i++ )
dp[i][i+1]=2;
for( r=3;r<=n;r++ ){
for( i=1;i<=n-r+1;i++ ){
j=i+r-1;
dp[i][j]+=( dp[i+1][j]+dp[i][j-1] )%mod;
for( k=i+1;k<=j-1;k++ )
if( !a[k-1]^a[k+1] ) //最后一个坐的人两边椅子同色
dp[i][j]=( dp[i][j]+dp[i][k-1]*dp[k+1][j]%mod*c[j-i][k-i]%mod )%mod;
}
}
printf("%lld\n",dp[1][n]);
}
return 0;
}
个人水平有限,如有任何问题欢迎在留言区指出,大家共同讨论!