• poj3718 Facer's Chocolate Dream


    题目链接

    正解:组合数+$dp$。

    今天考试的题,考试的时候感觉自己有点脑残过头了。。

    似乎发现了所有$1$其实都是一样的,然后不知道怎么强制每种物品只选一个。。

    然后就写了一个所有物品可以选任意个的$dp$,尝试与答案找一找规律,并没有找到,看完$std$发现只要再加一个转移就能过了。。所以还是讲讲正解吧。。

    首先所有$1$都是一样的,所以我们并不需要状压,直接开一个背包就行。

    设$f[i][j]$表示用了$i$个物品,$1$的个数为$j$的方案数,注意这个是有序状态,即使用顺序不同方案也不同。

    那么首先枚举当前这个物品让$1$的个数增加了多少,可能为$1,-1,3,-3$,这一步很容易转移。

    然后我们之前的转移是枚举的任意物品,必然会算重,而算重的充要条件就是$i$与之前某一个物品是一样的。

    我们先强制$i$与$i-1$是相同物品,那么我们可以用$i-2$的状态来转移到$i$,最后我们再乘一个$i-1$表示第$i-1$个物品实际上是可以插到前$i-1$个位置的任意一个的。

    最后由于我们算的是排列,所以还要再除以一个$m!$。

    通过这道题,我发现我还是太$naive$,见过的套路还是太少了,看来还是要深入学习各种计数的套路。。

     1 #include <bits/stdc++.h>
     2 #define il inline
     3 #define RG register
     4 #define ll long long
     5 #define rhl (10007)
     6 #define N (1005)
     7 
     8 using namespace std;
     9 
    10 int f[N][N],c[N][N],goal[N],n,m,st,fac;
    11 
    12 il int gi(){
    13   RG int x=0,q=1; RG char ch=getchar();
    14   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    15   if (ch=='-') q=-1,ch=getchar();
    16   while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    17   return q*x;
    18 }
    19 
    20 il char gc(){
    21   RG char ch=getchar();
    22   while (ch!='0' && ch!='1') ch=getchar();
    23   return ch;
    24 }
    25 
    26 il int qpow(RG int a,RG int b){
    27   RG int ans=1;
    28   while (b){
    29     if (b&1) ans=ans*a%rhl;
    30     if (b>>=1) a=a*a%rhl;
    31   }
    32   return ans;
    33 }
    34 
    35 il void work(){
    36   for (RG int i=0;i<n;++i) goal[i]=0; st=0;
    37   for (RG int i=0;i<n;++i) goal[i]^=gc()=='1';
    38   for (RG int i=0;i<n;++i) goal[i]^=gc()=='1';
    39   for (RG int i=0;i<n;++i) st+=goal[i]; f[0][st]=1;
    40   for (RG int i=(fac=1);i<=m;fac=fac*(i++)%rhl)
    41     for (RG int j=0;j<=n;++j){
    42       f[i][j]=0;
    43       if (j) f[i][j]=(1LL*c[j-1][1]*c[n-j+1][2]*f[i-1][j-1]+f[i][j])%rhl;
    44       if (j<n) f[i][j]=(1LL*c[j+1][2]*c[n-j-1][1]*f[i-1][j+1]+f[i][j])%rhl;
    45       if (j-3>=0) f[i][j]=(c[n-j+3][3]*f[i-1][j-3]+f[i][j])%rhl;
    46       if (j+3<=n) f[i][j]=(c[j+3][3]*f[i-1][j+3]+f[i][j])%rhl;
    47       if (i>1) f[i][j]=(f[i][j]-1LL*(c[n][3]-i+2)*f[i-2][j]*(i-1))%rhl;
    48     }
    49   printf("%d
    ",(f[m][0]+rhl)*qpow(fac,rhl-2)%rhl),f[0][st]=0; return;
    50 }
    51 
    52 int main(){
    53 #ifndef ONLINE_JUDGE
    54   freopen("cho.in","r",stdin);
    55   freopen("cho.out","w",stdout);
    56 #endif
    57   c[0][0]=1;
    58   for (RG int i=1;i<=1000;++i){
    59     c[i][0]=c[i][i]=1;
    60     for (RG int j=1;j<i;++j){
    61       c[i][j]=c[i-1][j-1]+c[i-1][j];
    62       if (c[i][j]>=rhl) c[i][j]-=rhl;
    63     }
    64   }
    65   while (scanf("%d%d",&n,&m)!=EOF && (n|m)) work();
    66   return 0;
    67 }
  • 相关阅读:
    分表分库-------shading jdbc使用
    字符串之特殊符号处理
    【汇编程序】编程将100到200中的奇数求和,结果送到SUM字单元
    【汇编程序】从键盘输入一个大写字母,将其转换成小写字母
    【汇编程序】统计非数字的个数
    【读书笔记】看过的书籍列表整理
    【微机原理】数字电路器件—门 与门 或门 非门电路及实例
    【c语言】递归题
    【汇编程序】BUF为首址的100个字节单元用原码表示的有符号数依次编程用补码表示的有符号数
    【汇编程序】编写一个完整的程序 将这3个数的最大者存放到MAX单元
  • 原文地址:https://www.cnblogs.com/wfj2048/p/8473743.html
Copyright © 2020-2023  润新知