Sample Input Output for Sample Input
3 2 2 sohelH-CSE samee-CSE 4 2 Blind-ECS sidky-CSE muntasir-CSC ShadowCoder-EEE 4 2 ABC-D1 DEF-D1 ghi-D2 jkl-D3 |
Case 1: 0 |
题意:给出n个人与这些人分别所属的公司,其中属于同一家公司的人不会超过3个人。将n个人任意排列(即有n!)中情况,按顺序将任意排列的n个人,每k个人一组(保证n%k==0),问满足每组不会有相同公司人的排列有多少个?
比赛的时候一直没往DP方面想,后来发现确实是一个很少见的dp或者是以前不知到的dp,想出了状态转移到是挺好想的了。
状态最后是间接经过肖神提示得到的,dp[i][j][p]表示还剩i个公司有3个人,j个公司有2个人,p个公司有1个人,的情况有多少种。
首先,最终要求的肯定就是dp[0][0][0]肯定很容易想了;初始值就是dp[x][y][z]=1,x,y,z就是给的情况
其次如果将i,j,p逆着递推,这样就正好得到最终结果,dp[i][j][p]可以有很多中转移情况,应为组成新的一组需要k个人,而这k个人是从i,j,p那一快转移过来都有可能,所以实现预处理好所以情况,到时候一一枚举就可以了;
最后转移的过程有很多排列数和组合数有区别的地方要注意,例如从i个人中如果要选ti个人,那么就会有C(i,ti)*(3^ti)种情况,对于j,p同理,最后选出来的k个人,同样也还是要有一种顺序而言,所以又是k!,写的过程中注意及时取模就ok了。
另外输入其实也看起来处理挺麻烦的,但其实可以用string中的substr,和find函数,然后就挺好写了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<map> 7 #define see(x) cout<<#x<<":"<<x<<endl; 8 using namespace std; 9 const int maxn = 105; 10 const int mod = 1234; 11 int dp[maxn][maxn][maxn], cntt[maxn], c[maxn][maxn], way[maxn][5]; 12 int exp22[maxn], exp3[maxn], f[maxn]; 13 string str, tmp; 14 map<string,int> cha; 15 int main(){ 16 int t, i, j, p, l, m, cas=1, n, k, cnt, ti, tj, tp; 17 int x, y, z; 18 c[0][0]=1; 19 for(i=1;i<maxn;i++){ 20 c[i][0] = 1; 21 for(j=1;j<=i;j++){ 22 c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod; 23 } 24 } 25 exp22[0] = exp3[0] = f[0] = 1; 26 for(i=1;i<maxn;i++){ 27 exp22[i] = (exp22[i-1]*2)%mod; 28 exp3[i] = (exp3[i-1]*3)%mod; 29 f[i] = (f[i-1]*i)%mod; 30 } 31 scanf("%d",&t); 32 while(t--){ 33 scanf("%d%d",&n,&k); 34 m = 0; 35 for(i=0;i<=k;i++){ 36 for(j=0;j<=k;j++){ 37 for(p=0;p<=k;p++){ 38 if((i+j+p)==k){ 39 way[m][1] = i; 40 way[m][2] = j; 41 way[m][3] = p; 42 m++; 43 } 44 } 45 } 46 } 47 cha.clear(); 48 cnt=0; 49 memset(cntt,0,sizeof(cntt)); 50 x=y=z=0; 51 for(i=0;i<n;i++){ 52 cin>>str; 53 p = str.find('-'); 54 tmp = str.substr(p+1,str.size()-p-1); 55 if(!cha[tmp]){ 56 cha[tmp] = ++cnt; 57 cntt[cnt]++; 58 } 59 else{ 60 cntt[cha[tmp]]++; 61 if(cntt[cha[tmp]]==3){ 62 z++; 63 } 64 } 65 y = n-z*2-cnt; 66 x = cnt-z-y; 67 } 68 memset(dp,0,sizeof(dp)); 69 dp[z][y][x] = 1; 70 for(i=n;i>=0;i--){ 71 for(j=n;j>=0;j--){ 72 for(p=n;p>=0;p--){ 73 if(dp[i][j][p]){ 74 for(l=0;l<m;l++){ 75 ti = way[l][1]; tj = way[l][2]; tp = way[l][3]; 76 if(i>=ti&&j>=tj&&p>=tp){ 77 dp[i-ti][j+ti-tj][p+tj-tp] += dp[i][j][p]*c[i][ti]%mod*exp3[ti]%mod*c[j][tj]%mod*exp22[tj]%mod*c[p][tp]%mod*f[k]%mod; 78 dp[i-ti][j+ti-tj][p+tj-tp] %= mod; 79 } 80 } 81 } 82 } 83 } 84 } 85 printf("Case %d: %d\n",cas++,dp[0][0][0]); 86 } 87 return 0; 88 }