zoukankan      html  css  js  c++  java
  • 【Tsinghua OJ】多米诺骨牌(domino)问题

    (domino.c/cpp)
    【问题描述】
      小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种。她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的。现在她有n个骨牌要来排列,她想知道不美观的排列的个数。由于数字较大,数学不好的 她不会统计,所以请你来帮忙。希望你帮她求出不美观的排列的个数。

    【输入数据】
      只有一个正整数,即要排列的骨牌个数。
    【输出数据】
      一个数,即不美观的排列个数。
    【样例输入】
    4
    【样例输出】
    6
    【样例解释】
      有四种不美观的排列。
      黑黑黑黑,白白白白,黑黑黑白,白白白黑,黑白白白,白黑黑黑
    【数据范围】
      20%的数据,n<=60;
      50%的数据,n<=6000;
      100%的数据,n<=10000。

      时间限制: 1 sec
      空间限制: 256 MB
    【提示】
      动态规划、高精度加法。

    —————————————————————————————————

    【solution】
    虽然只是Tutorial里面的题,虽然听说现在是小学僧的练习题(T_T),不过还真是想了辣么一会儿。算算真是已经有4年多没碰过这些东西了,为了完成这门课作业也真是找回了当初的感觉,真是怀念这种一道一道题“过关斩将”的感觉,已经很久不曾有这种感觉了。

    回到正题。这道题初看很容易去正向考虑如何统计“不美观”的排列个数,甚至会误入使用组合数学的错误算法。根据提示,往动态规划方面想,会发现,实 际上,这道题需要反向来思考,即考虑“美观”的排列个数。那么,题目转化为求解连续颜色不超过3(不包括3)的排列个数 a,然后再用所有的排列个数(2^n)减去 a 即得问题解。再细想,这不跟动态规划的经典问题——上楼梯问题 很像吗?

    于是,问题得解:
    对于每一个色块(连续的 1 个或者 2 个相同颜色的白色或者黑色色块),就相当于上楼梯问题中的上升一阶或者两阶,所以这里其实我们完全可以忽略到颜色这个因素(最后再把得到的上阶梯的总数乘 以2,因为把所有的色块全部反转一次颜色都可以得到原来那种的状态的 twin solution,而上楼梯问题并未考虑颜色问题,只是简单的划分为一次动作,这个问题正是因为颜色来划分的),而是把一个色块等同为上楼梯问题中的一次 动作。
    状态方程为:f[n] = f[n-1] + f[n-2]。初始条件 f[1] = 1; f[2] = 2。
    也就是不严格对应项数的著名的斐波拉契数列。
    最后的结果为 2^n - 2*f[n]。

    由于问题数据规模较大,最后还要用高精度加法来实现。

    【source code】

      1 #include <stdio.h> 
      2 
      3 #define L 6001
      4 #define wei 208 
      5 
      6 void echo(int ans)        //make sure printing a 4-wei number,此函数可以用格式控制方式: printf("%04d", ans); 简单代替。C++中类似使用setfill('0') setw(30)等
      7 {
      8     if (ans > 999)
      9     {
     10         printf("%d", ans);
     11     }
     12     else if (ans > 99)
     13     {
     14         printf("0%d", ans);
     15     }
     16     else if (ans > 9)
     17     {
     18         printf("00%d", ans);
     19     }
     20     else
     21     {
     22         printf("000%d", ans);
     23     }
     24 } 
     25 
     26 int main(void)
     27 {
     28     int n, i, j, temp, pro = 0, cn = 0, an[L] = { 0 }, a[L][wei] = { 0 }, c[wei] = { 0 }, ans[wei] = { 0 };
     29     bool zero = false; 
     30 
     31     scanf("%d
    ", &n);  
     32 
     33     //bases for a, c and an, cn
     34     a[3][0] = 3; a[2][0] = 2; c[0] = 1;  
     35 
     36     //a[n] = a[n-1] + a[n-2]
     37     for (i = 4; i <= n; i++)
     38     {
     39         //Gao Jin Du Jia Fa
     40         pro = 0;
     41         for (j = 0; j <= an[i - 1]; j++)
     42         {
     43             temp = a[i - 1][j] + a[i - 2][j] + pro;
     44             a[i][j] = temp % 10000;
     45             pro = temp  / 10000;
     46         }
     47         if (pro > 0)
     48         {
     49             a[i][j] = pro;
     50             an[i] = j;
     51         }
     52         else an[i] = an[i - 1];
     53     } 
     54 
     55     // 2^n
     56     for (i = 0; i < n; i++)
     57     {
     58         //Gao Jin Du Jia Fa
     59         pro = 0;
     60         for (j = 0; j <= cn; j++)
     61         {
     62             temp = c[j] * 2 + pro;
     63             c[j] = temp % 10000;
     64             pro = temp / 10000;
     65         }
     66         if (pro > 0)
     67         {
     68             c[j] = pro;
     69             cn++;
     70         }
     71     } 
     72 
     73     //ans = 2^n - a[n] *2, Gao Jin Du Jia Fa
     74     pro = 0;
     75     for (j = 0; j <= an[n]; j++)
     76     {
     77         temp = a[n][j] * 2 + pro;
     78         a[n][j] = temp % 10000;
     79         pro = temp / 10000;
     80     }
     81     if (pro > 0)
     82     {
     83         an[n]++;
     84         a[n][j] = pro;
     85      } 
     86 
     87     pro = 0;
     88     for (j = 0; j <= cn; j++)
     89     {
     90         temp = c[j] - a[n][j] + pro;
     91         if (temp < 0)
     92         {
     93             ans[j] = temp + 10000;
     94             pro = -1;
     95         }
     96         else
     97         {
     98             ans[j] = temp;
     99             pro = 0;
    100         }
    101     } 
    102 
    103     //print the answer, ignoring the zeros in the front
    104     for (i = cn; i >= 0; i--)
    105     {
    106         if (!zero)
    107         {
    108             if (ans[i] != 0)
    109             {
    110                 printf("%d", ans[i]);
    111                 zero = true;
    112             }
    113         }
    114         else echo(ans[i]);
    115     }
    116     printf("
    "); 
    117 
    118     return 0;
    119 }

    【代码改进空间】
    1、将高精度算法函数化;
    2、仍然只能通过 Tsinghua Online Judge 40%的数据,其他数据都是Runtime error (exitcode: 11),暂无果。

    【优化后AC的代码】
    感谢@Plan能抽出时间来AC这道题,同时找到了字符串的高精度加法解决办法,过了100%的数据。以下是参考了她的代码后自己重新几乎是照着写的代码(求2^n的函数从递归形式改成了循环版):

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>  
      4 
      5 char *add(char a[], char b[])
      6 {
      7     int len, i, j, k, up, x, y, z;
      8     char *c, *back;
      9 
     10     len = (strlen(a) > strlen(b)) ? strlen(a) + 2 : strlen(b) + 2;
     11     c = (char *)malloc(len*sizeof(char));
     12     back = (char *)malloc(len*sizeof(char));
     13 
     14     i = strlen(a) - 1;
     15     j = strlen(b) - 1;
     16     k = 0; up = 0;
     17 
     18     while (i >= 0 || j >= 0)
     19     {
     20         if (i<0) x = '0'; else x = a[i];
     21         if (j<0) y = '0'; else y = b[j];
     22         z = x - '0' + y - '0';
     23         if (up == 1) z += 1;
     24         if (z>9) 
     25         { 
     26             up = 1; z %= 10; 
     27         }
     28         else up = 0;
     29         c[k++] = z + '0';
     30         i--; j--;
     31     }
     32     if (up) c[k++] = '1';
     33     c[k] = ''; 
     34     
     35     //reverse
     36     i = 0;
     37     for (k -= 1; k >= 0; k--) back[i++] = c[k];   
     38     back[i] = '';
     39 
     40     return back;
     41 }
     42 
     43 char *sub(char a[], char b[])
     44 {
     45     int len, i, j, k, down, x, y, z;
     46     char *c, *back;
     47 
     48     len = strlen(a);
     49     c = (char *)malloc(len*sizeof(char));
     50     back = (char *)malloc(len*sizeof(char));
     51 
     52     i = strlen(a) - 1;
     53     j = strlen(b) - 1;
     54     k = 0; down = 0;
     55 
     56     while (i >= 0 || j >= 0)
     57     {
     58         if (i<0) x = '0'; else x = a[i];
     59         if (j<0) y = '0'; else y = b[j];
     60         z = x - '0' - (y - '0') - down;
     61         if ( z < 0 )
     62         {
     63             down = 1;
     64             z = z + 10;
     65         }
     66         else down = 0;
     67         c[k++] = z + '0'; 
     68         i--; j--;
     69     }
     70     while (c[--k] == '0') ;
     71 
     72     //reverse
     73     i = 0;
     74     for (k; k >= 0; k--)
     75     {
     76         back[i++] = c[k];
     77     }
     78 
     79     return back;
     80 }
     81 
     82 char *power(int n)
     83 {
     84     int i;
     85     char *temp="2";
     86     
     87     for (i = 2; i <= n; i++)
     88     {
     89         temp = add(temp, temp);
     90     }
     91 
     92     return temp;
     93 }
     94 
     95 char *fib(int n)
     96 {
     97     char *p = "1", *q = "1";
     98     char *s = "1";
     99     int i;
    100 
    101     for (i = 0; i < n - 1; i++)
    102     {
    103         s = add(p, q);
    104         p = q;
    105         q = s;
    106     }
    107 
    108     return s;
    109 }
    110 
    111 int main()
    112 {
    113     int n;
    114     char *mi, *f;
    115 
    116     scanf("%d
    ", &n);
    117 
    118     mi = power(n);
    119     f = fib(n);
    120     f = add(f, f);
    121 
    122     printf("%s
    ", sub(mi, f));
    123 
    124     return 0;
    125 }

    【参考资料】
    1:http ://www.cnblogs.com/kuangbin/archive/2011/07/22/2113836.html 高精度加法的C++实现;
    2:http://blog.sina.com.cn/s/blog_993d2542010143qw.html Fibonacci数列的第N项 log(N)算法(未用到)。

    有几点:
    1)由于数据规模,四位进一次位的int版高精也无法AC掉所有数据,只能用string来解决了。
    2)要注意高精度运算string的顺序是不是跟数字顺序一致,所以代码中有reverse操作。


    [ by Maples7 ]
    [ Copyright @Maples7,转载请注明出处。 ]
  • 相关阅读:
    黄聪:解决Bootstrap模态框(modal)弹出后页面跑到顶部的办法
    黄聪:visual studio 2017编译运行出现脚本发生错误等问题如何解决?
    黄聪:xampp启动后mysql报Error
    黄聪:公众号怎么用微信做出点击此处查看答案
    黄聪:保持web页面生成的app一直处于用户登录状态不退出
    黄聪: $(document).click() 在iphone上不触发事件解决办法
    黄聪:bootstrap的模态框modal插件在苹果iOS Safari下光标偏离问题解决方案
    黄聪:pjax使用心得总结
    黄聪:Pjax无刷新跳转页面实现,支持超链接与表单提交
    黄聪:Pjax 无刷新开发web,更好用户体验
  • 原文地址:https://www.cnblogs.com/maples7/p/4040058.html
Copyright © 2011-2022 走看看