• [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

     

    越努力 越幸运
  • 相关阅读:
    高性能NoSql数据库 SSDB
    Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory
    Java基础-SSM之Spring的POJO(Plain Old Java Object)实现AOP
    Java基础-SSM之Spring的AOP编程
    Java基础-SSM之Spring快速入门篇
    Java基础-SSM之mybatis的统计函数和分页查询
    Java基础-SSM之mybatis一对一关联
    Java基础-SSM之mybatis多对多关联
    Java基础-SSM之mybatis的树形控件(自关联)
    Java基础-SSM之mybatis一对多和多对一关系映射
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11158761.html
Copyright © 2020-2023  润新知