[2013腾讯马拉松复赛第一场]HDU 4532 湫秋系列故事——安排座位

一道典型的组合问题,可惜当时想的做法就不对,一直走到黑了。当时想的做法是用容斥做,可惜当时容斥的姿势不对,首先先把问题看成是组合问题最后再转化为排列,枚举每组是否出现挨着的,枚举到头求组合,由于可能统计重复,所以容斥。这种思想明显漏算了很多情况,当时还脑残的认为肯定正确……。

想了半天都没想到怎么做,于是去膜拜大神的BLOG去了,虽然只有精简的几行,而且没看懂具体怎么做的,不过思路瞬间成竹在胸了。

正解用组合DP做,因为状态能够递推,还是先把排列问题转化成组合问题,最后再乘各组的阶乘。解决组合类问题的思想其实还是那几种,插空、捆绑、隔板什么的。

设dp[i][j]为第i组人员加入到队列中后,有j个空的两边是同一组的人。

那么枚举i-1组 j所有的状态j,枚举当前组有k个空是左右人同组的(捆绑或隔板),枚举这几个人插入了j`中的几个空l(插空)。

这样新的j的状态就是j+k-l,状态也就可以转移了。

View Code
 1 #include <map>
 2 #include <set>
 3 #include <list>
 4 #include <queue>
 5 #include <stack>
 6 #include <cmath>
 7 #include <ctime>
 8 #include <vector>
 9 #include <bitset>
10 #include <cstdio>
11 #include <string>
12 #include <numeric>
13 #include <cstring>
14 #include <cstdlib>
15 #include <iostream>
16 #include <algorithm>
17 #include <functional>
18 using namespace std;
19 typedef long long ll;
20 #define Exp 1e-8
21 #define inf 0x7fffffff
22 #define read freopen("in.txt","r",stdin)
23 #define write freopen("out.txt","w",stdout)
24 #define maxn 55
25 #define mod 1000000007
26 ll num[maxn];
27 ll dp[maxn][505];
28 ll muti_mod(ll a,ll b,ll n)
29 {
30     ll exp=a%n,res=0;
31     while(b)
32     {
33         if(b&1){res+=exp;if(res>=n)res-=n;}
34         exp<<=1;
35         if(exp>=n)exp-=n;
36         b>>=1;
37     }
38     return res;
39 }
40 ll con[505][505],mul[maxn];
41 void init()
42 {
43     memset(con,0,sizeof(con));
44     con[0][0]=1;
45     for(int i=1;i<505;i++)
46     {
47         con[i][0]=con[i][i]=1;
48         for(int j=1;j<i;j++)con[i][j]=(con[i-1][j-1]+con[i-1][j])%mod;
49     }
50     mul[0]=1;
51     for(int i=1;i<maxn;i++)mul[i]=muti_mod(mul[i-1],i,mod);
52 }
53 int main()
54 {
55     //read;
56     init();
57     int cas,n;
58     scanf("%d",&cas);
59     for(int x=1;x<=cas;x++)
60     {
61         scanf("%d",&n);
62         for(int i=1;i<=n;i++)scanf("%I64d",&num[i]);
63         memset(dp,0,sizeof(dp));dp[0][0]=1;
64         ll sum=0,m=1;
65         for(int i=1;i<=n;i++)
66         {
67             ll t=num[i];
68             for(int j=0;j<=sum;j++)if(dp[i-1][j])
69             {
70                 for(int k=1;k<=t;k++)
71                     for(int l=0;l<=j&&l<=k;l++)
72                     {
73                         ll tmp=dp[i-1][j]*con[t-1][k-1]%mod*con[j][l]%mod*con[sum+1-j][k-l]%mod;
74                         dp[i][j+(t-k)-l]=(dp[i][j+(t-k)-l]+tmp)%mod;
75                     }
76             }
77             sum+=num[i],m=muti_mod(m,mul[t],mod);
78         }
79         ll ans=muti_mod(dp[n][0],m,mod);
80         printf("Case %d: %I64d\n",x,ans);
81     }
82     return 0;
83 }
原文地址:https://www.cnblogs.com/mcflurry/p/2991960.html