zoukankan      html  css  js  c++  java
  • [CQOI2011]放棋子(组合数+dp)(考试)

    题干:
      

      输入第一行为两个整数n, m, c,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm。N,M<=30 C<=10 总棋子数有大于250的情况。输出仅一行,即方案总数除以 1000000009的余数。

    题解:

    20%暴搜

       考试时还被认真看着道题就结束了。。。本来我还可以暴搜dfs填表骗二十分。。。暴力没什么技术含量,放一个代码有兴趣的可以看看。。。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 using namespace std;
     4 const int mod=1e9+7;
     5 int ans,c,n,m,s[15],a[35],b[35];
     6 inline void dfs(int x,int y){
     7     if(x==n+1){
     8         for(register int i=1;i<=c;i++) if(s[i]) return;
     9         ans++;  ans%=mod;
    10         return;
    11     }
    12     for(register int i=1;i<=c;i++){
    13         if(a[x]!=i&&a[x]!=0) continue;
    14         if(b[y]!=i&&b[y]!=0) continue;
    15         int ta=a[x],tb=b[y];
    16         a[x]=i;  b[y]=i;  s[i]--;
    17         if(y==m) dfs(x+1,1);
    18         else dfs(x,y+1);
    19         a[x]=ta;  b[y]=tb;  s[i]++;
    20     }
    21     if(y==m) dfs(x+1,1);
    22     else     dfs(x,y+1);
    23 }
    24 signed main(){
    25     scanf("%d%d%d",&n,&m,&c);
    26     for(register int i=1;i<=c;i++) scanf("%d",&s[i]);
    27     dfs(1,1);
    28     printf("%d",ans);
    29 }
    View Code

    100% dp O(n2 *m2 *c)

      正解太难想了!!!

      我们将dp式定义为:前k种棋子占了i行j列时的方案数(i,j表所占行与列的数量,而不是占了1~i行,1~j列)。有了这个定义我们就可以较轻松地(难爆了)能想到:

    dp[i][j][k]=ΣiL=1ΣjR=1dp[L][R][k-1]*g[i][j][k]*C(i-L,n-L)*C(j-R,m-R);

      其中g[i][j][k]表示x个同颜色的棋子放在共i行、j列(i、j意思同dp式)时的方案数,

      C(x,y)为组合数Cxy

      解释一下:我们现在放的第k种棋子数量不一定,所以我们可以从占了1列1行、占了1列2行、占了2列1行、占了2列2行、...、占了i-1列j行、占了i列j-1行转移过来。(作者在这并没有考虑占了i列j行的情况,因为我们现在更新的不就是占了i列j行的情况吗。。。在这里不考虑这种情况并不代表就会有遗漏,而是还没有来得及考虑,不会对全局造成影响针对这两个组合数,其实就是从n-L个未被选择的横行中选出(i-L)个,因为我们设定的的是搁上这第k种棋子后共占了i行,所以就有(i-L)行由第k种棋子所占(纵行同理)。 

      现在,我们已经将框架建好,就差一个g[i][j][k]x个同颜色的棋子放在共i行、j列(i、j意思同dp式)时的方案数)。

      按照平常思路,我们尝试着用小的部分加和来求,发现需要考虑的情况过多,也难以实现(其实是懒得想)。我们尝试着用一下单步容斥——用所有可能情况减去不合法的情况。即:

    g[i][j][k]=C(k,i*j)-ΣiL=1ΣjR=1 (g[L][R][k]*C(L,i)*C(R,j))

      C(k,i*j):在i*j个格子里放k个棋子的方案数。可能有人会问了,不是说g[i][j][k]表示的是任意i行j列吗?怎么又放到i*j的格子里了?其实我们仔细想想就可知道:要想让这k个棋子占且只占掉i行j列其实就是相当于向这随便选出的i行j列中相交的格子里放棋子(假设将这选出的i行j列挤成连续的一块,分开也一样),也就有了开始时说的在i*j个格子里放k个棋子

      C(L,i):从现在选出的i行中挑出L行的方案数。它其实乘上C(R,j)后作为了g[L][R]的系数。那减去{ ΣiL=1ΣjR=1 (g[L][R]*C(L,i)*C(R,j)) }这一坨做什么呢?其实就是减去不合法的情况——在i行j列中存在没有占到的行或列的情况。又因为在这i行j列中共有C(L,i)*C(R,j)种只占了L行R列的不合法的情况,所以减去即可。(C(R,j)同理)

      又因为答案最后并没有要求全部占满,所以所有情况都需考虑,即:

    ans=Σni=1Σmj=1 dp[i][j][c]

      (这道题的组合数可以用杨辉三角递推求得)

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #define up(i,x,y,z) for(register int i=(x);i<=(y);i+=(z))
     4 #define $ 50
     5 #define ll long long
     6 #define mod 1000000009
     7 using namespace std;
     8 ll m,n,k,t,C[$*$][$*$],dp[$][$][$],a[$][$],c,aa[$],ans;
     9 signed main(){
    10     scanf("%lld%lld%lld",&n,&m,&c);
    11     up(i,1,c,1) scanf("%lld",&aa[i]);
    12     up(i,0,2000,1){
    13         C[i][0]++;
    14         up(j,1,i,1) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    15     }
    16     dp[0][0][0]=1;
    17     up(k,1,c,1){
    18         memset(a,0,sizeof(a));
    19         up(i,0,n,1) up(j,0,m,1){
    20             if(i*j>=aa[k]){
    21                 a[i][j]=C[i*j][aa[k]];
    22                 up(l,0,i,1) up(r,0,j,1)
    23                     if(l!=i||r!=j) 
    24                         a[i][j]=(a[i][j]-a[l][r]*C[i][l]%mod*C[j][r]%mod+mod)%mod;
    25             }
    26         }
    27         up(i,0,n,1) up(j,0,m,1) up(l,0,i,1) up(r,0,j,1)
    28             if(l!=i||r!=j) 
    29                 dp[i][j][k]=(dp[i][j][k]+
    30                              dp[l][r][k-1]*a[i-l][j-r]%mod*C[n-l][i-l]%mod*
    31                              C[m-r][j-r]%mod+mod)%mod;
    32     }
    33     up(i,1,n,1) up(j,1,m,1) ans=(ans+dp[i][j][c])%mod;
    34     printf("%lld",ans);
    35 } 
    View Code

     

    越努力 越幸运
  • 相关阅读:
    Oracle 按一行里某个字段里的值分割成多行进行展示
    Property or method "openPageOffice" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by
    SpringBoot 项目启动 Failed to convert value of type 'java.lang.String' to required type 'cn.com.goldenwater.dcproj.dao.TacPageOfficePblmListDao';
    Maven 设置阿里镜像
    JS 日期格式化,留作参考
    JS 过滤数组里对象的某个属性
    原生JS实现简单富文本编辑器2
    Chrome控制台使用详解
    android权限(permission)大全
    不借助第三方网站四步实现手机网站转安卓APP
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11158761.html
Copyright © 2011-2022 走看看