文章目录

  • 题目描述
  • 解题思路
  • 喜闻乐见的AC代码
  • 补充一:高精度
  • 补充二:状态压缩
  • 完整注释代码
  • 后记

题目描述

选修程序设计和算法课程的学生人数为 n,任课老师设置了 m 道练习题目(其中:1 <= m,n <= 100),假定每道题的难度和知识点都是一样的,要求选修本课程的同学利用在线测评系统完成一道题目,同时还要求每道题目至少要被做过一次,问有多少种做题方案?当然也有可能没有一种方案存在。

输入描述

多组输入,每一行输入两个用一个空格分开的整数 n 和 m

输出描述

占一行,对应输入的每组数,输出对应的方案数

样例

4 2
15 12

7
106470

解题思路

粗略描述一下这道屑题

本题的表达比较反人类,它是说把n个不同的物件放入m个相同的篮子,并且要求篮子不能为空。
(关键句是“每道题难度和知识点都是一样的”-_-|||)

从高中正常毕业的学生的想法

(如果你已经通过百度了解“第二类斯特林公式”,请跳过这个屑过程)
利用高中组合数知识解说一下样例:
样例一:
4个物品放入2个篮子,可以3+1分或2+2分,因此我们的answer为:

样例二:
15个物品放入12个篮子有三种分法,
4+1+1+1+1+1+1+1+1+1+1+1;
3+2+1+1+1+1+1+1+1+1+1+1;
2+2+2+1+1+1+1+1+1+1+1+1;
因此answer’为:

(看不懂为什么除以2!或3!的可以退学了 [x)
那么问题来了,实现这样的思考过程十分麻烦,而且计算机执行起来效率也不高,不符合“计算机的思维”。

符合计算机思维的思路

我们知道组合数是可以递推出来的,实际上这道屑题也存在这样的状态转移。
对于n个物品和m个篮子,可以这么考虑答案组成;
1.来自n-1个物品m-1个篮子。
相当于在这种状态下加上一个装有第n个物品的篮子;
2.来自n-1个物品m个篮子。
添加第n个物品,这个物品有m种选择,它可以放在这m个篮子里任意一个中
(思考一下m种方案是否和m个相同的篮子矛盾?为什么?)

由此得出一个简单的递推式:

如果m=1或m=n时,answer=1;
如果n>m时,answer=0;

喜闻乐见的AC代码

这样就能AC了?

#include<stdio.h>
#include<string.h>
#define min(x,y) ((x)<(y)?(x):(y))
int main()
{int i,j,n,m,k,flag;int ans[121][101];while(~scanf("%d %d",&n,&m)){memset(ans,0,sizeof(ans));for(i=1;i<=n;++i){ans[1][1]=1;for(j=min(i-1,m);j>=1&&n-i>=m-j;--j){for(k=1;k<=120;++k)ans[k][j]=ans[k][j-1]+j*ans[k][j];for(k=1;k<=120;++k)if(ans[k][j]>=10){       ans[k+1][j]+=ans[k][j]/10;ans[k][j]%=10;}}ans[1][i]=1;}flag=120;for(k=120;k>=0;--k)if(ans[k][m]!=0){flag=k;break;}if(flag==120)flag=1;for(k=flag;k>=1;--k)printf("%d",ans[k][m]);printf("\n");}return 0;
}

是不是感觉代码和说好的思路有点出入。

补充一:高精度

——补充一 高精度——
事实证明,我们输入100 50时,数据是很大的,如果不用高精度,妥妥的超出范围。

——高精度的实质就是用数组暴力模拟竖式计算
比如我们计算两个100位数a和b的乘积,可以用字符串读取a和b,然后把用b数组中的最后一位乘以a中的每一位数保存入数组ans的对应位置,并做好进位工作。依次循环,直到b的第一位数乘完数组a中每一个元素。
以下几种高精度参考:
——高精度加法:

#include<stdio.h>
#include<string.h>
int ans[510];//保存答案
char a[510],b[510];//读入数据
int a1[510],b1[510];//转为整型数组保存
int main()
{int i,flag,l1,l2,j;while(~scanf("%s %s",a,b)){memset(ans,0,sizeof(ans));memset(a1,0,sizeof(a1));memset(b1,0,sizeof(b1));//*************************//j=0; for(i=strlen(a)-1;i>=0;--i)a1[j++]=a[i]-'0';j=0;                       //分别把字符串a和字符串b中的每一个元素逆序装入整型数组a1和b1中 for(i=strlen(b)-1;i>=0;--i)b1[j++]=b[i]-'0';//*************************//for(i=0;i<510;++i){ans[i]+=a1[i]+b1[i];if(ans[i]>=10){                      //逐位做加法并进位 ans[i+1]+=ans[i]/10;ans[i]%=10;}}//*************************// flag=509;for(i=509;i>=0;--i)if(ans[i]!=0){flag=i;           //找到最高位元素的位置,记录以用于接下来输出答案 break;}//************************//if(flag==509){printf("0\n");}else{for(i=flag;i>=0;--i)printf("%d",ans[i]);printf("\n");}}return 0;
}

——高精度乘法:

#include<stdio.h>
#include<string.h>
char a[101],b[101];//读入数据
long long ans[302];//答案数据
int main()
{int i,j,la,lb,k,flag;while(~scanf("%s\n%s",&a,&b)){la=strlen(a);lb=strlen(b);memset(ans,0,sizeof(ans));//************************************************// for(i=la-1;i>=0;--i){for(j=lb-1;j>=0;--j)ans[la-1-i+lb-j-1]+=(a[i]-'0')*(b[j]-'0');for(k=la-i-1;k<=la-i-1+lb-1;++k)if(ans[k]>=10)                            //倒过来从最小的位数开始乘,加到对应位置上,并且进行一次进位 {                                         //实际上一次性进位+找到最高位更快,但这么写更符合人类的正常思维 ans[k+1]+=ans[k]/10;ans[k]%=10;}}//************************************************//for(i=301;i>=0;--i)if(ans[i]!=0){flag=i;                                     //找最高位 break;}//************************************************// for(i=flag;i>=0;--i)printf("%lld",ans[i]);printf("\n");}
}

本题中利用高精度计算的代码段:

for(j=min(i-1,m);j>=1&&n-i>=m-j;--j)
{for(k=1;k<=120;++k)ans[k][j]=ans[k][j-1]+j*ans[k][j];for(k=1;k<=120;++k)if(ans[k][j]>=10){     ans[k+1][j]+=ans[k][j]/10;ans[k][j]%=10;}
}

补充二:状态压缩

——补充二 状态压缩——
注意本题只有8MB空间限制,开一个100 * 100 * 100的数组保存每一行每一列的高精度数有够呛的。
事实上,我们没必要一定要存储之前的数据,毕竟我们的递推式只涉及到n和n-1这两行,前面的数据完全可以舍弃。

假设现在正在用k递推k+1,由之前的递推式可知状态转移是从(k,m-1)+m*(k,m) 到(k+1,m),我们完全可以只开一维的高精数组(即100 * 100),把每一次计算出来的(k+1,m)取代掉上一行同位置的(k,m),也就是说我们的这个一维高精度数组保存的数据由第k+1行覆盖掉第k行。

如何覆盖?
如果仍旧以人类的思维,从第一个数据开始覆盖是会出错的。

但是从最后一个数据开始覆盖就不会出错:

状态压缩其实还有另外一层意义
从后面开始覆盖,意味着有些数据我们用不着计算了,毕竟答案只是一个点而已。

完整注释代码

以下:

#include<stdio.h>
#include<string.h>
#define min(x,y) ((x)<(y)?(x):(y))
int main()
{int i,j,n,m,k,flag;int ans[121][101];//一维高精度数组,只记录某一行的状态 while(~scanf("%d %d",&n,&m)){memset(ans,0,sizeof(ans));for(i=1;i<=n;++i){ans[1][1]=1;//对无法状态转移的数据初始化 for(j=min(i-1,m);j>=1&&n-i>=m-j;--j)//限制计算域 {//**********************************************// for(k=1;k<=120;++k)ans[k][j]=ans[k][j-1]+j*ans[k][j];for(k=1;k<=120;++k)if(ans[k][j]>=10)                       //高精度计算 {      ans[k+1][j]+=ans[k][j]/10;ans[k][j]%=10;}//**********************************************//}ans[1][i]=1;//对无法状态转移的数据初始化 }flag=120;for(i=120;i>=0;--i)if(ans[i][m]!=0)                                //找到最高位 {flag=i;break;}//**********************************************//if(flag==120)flag=1;for(i=flag;i>=1;--i)printf("%d",ans[i][m]);printf("\n");}return 0;
}

本代码占空间1.51MB,耗时0MS

后记

这个屑题本身不是很难,主要是有一些小优化值得记录一下。
水得题解++;
2018年12月26日

TSOJ 好好做题(屑)——递推状态压缩+高精度相关推荐

  1. BZOJ-4300 绝世好(蛋疼)题 DP(递推)

    翻zky学长的blog时翻出来的..... 4300: 绝世好题 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 736 Solved: 393 [Sub ...

  2. hdu 1297 递推难题

    这题的话,我能玩一年 今天做了很多递推的题,这题无疑是最复杂的 其实可以看出来,2,3,4,5为一类,不妨定义为2型,1,6为一类,定义为1型 规定num[i]为结尾是i的凹槽的数量 我们可以能轻易的 ...

  3. 递推算法与递推套路(手撕算法篇)

    联系我们:有道技术团队助手:ydtech01 / 邮箱:[ydtech@rd.netease.com] 之前学习基础知识的时候也说了,递推和动态规划有这暧昧不清的关系,可以说,动态规划就是多了一个决策 ...

  4. 递推算法与递推套路(算法基础篇)

    联系我们:有道技术团队助手:ydtech01 / 邮箱:[ydtech@rd.netease.com] 相信了解算法同学经常会说动态规划太难了,看到题目完全不知从何下手,或者是说"一看题解就 ...

  5. P1759 通天之潜水(不详细,勿看)(动态规划递推,组合背包,洛谷)

    题目链接:点击进入 题目分析: 简单的组合背包模板题,但是递推的同时要刷新这种情况使用了哪些物品 ac代码: #include<bits/stdc++.h> using namespace ...

  6. 震惊!递推与递归竟然可以这么编!%99的程序员都不知道!

    四.归并排序(逆序对) (一).归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全 ...

  7. C++奥赛一本通递推题解

    title: C++奥赛一本通刷题记录(递推) date: 2017-11-08 tags: 一本通 openjudege categories: OI C++奥赛一本通刷题记录(递推) 2017.1 ...

  8. 怎么判断第几范式例题_学完就忘、做题就懵!初级会计怎么备考才能更高效?...

    备考初级会计之路真是艰辛,目前官方政策初级会计职称是一年一考,早早开始学吧,又有惰性,总觉得时间还很多,报完名再开始学吧,又总是搞不透彻知识点,一个知识点需要一天甚至更多才能消化...... 很多时候 ...

  9. 【转】状态压缩动态规划

    引入  首先来说说"状态压缩动态规划"这个名称,顾名思义,状态压缩动态规划这个算法包括两个特点,第一是"状态压缩",第二是"动态规划". 状 ...

最新文章

  1. 即将到截止日期,Frontiers期刊向您邀稿啦!脑机接口领域
  2. 在servlet中设置的字符编码集为什么还会出现乱码(亲测)
  3. java继承上机作业
  4. 可编程交换时代就在这里
  5. 华为交换机默认vlan都是通的吗_华为设备二层交换技术——Hybrid接口详解
  6. phpmyadmin的安装和使用
  7. 谷歌地图自定义popup框
  8. 中信银行Java笔试题库,手撕面试官
  9. SOUI知识点小结2
  10. O2O电子商务盈利模式是什么 O2O电子商务模式遇到的困难有哪些?
  11. Centos版Linux 一些常用操作命令 收集
  12. Selenium显示等待和隐式等待
  13. vue 总结一项目建立及文件夹结构配置
  14. python操作xlsx格式文件
  15. 【玖哥乱弹】神通广大的JavaScript
  16. MultipartFile和File互转
  17. 触动传媒总部人去楼空 上海多家出租车公司追讨欠款
  18. 日本美术学校有哪些,日本美术学校排名榜
  19. textpad java sdk_如何防止TextPad在运行Java时创建临时批处理文件?
  20. Android 刷机/Root/安装Xposed

热门文章

  1. 查找数据库指定数据的数据表和字段名称SQL语句
  2. matlab用switch画出分段函数,matlab用switch语句绘出分段函数 在 的图像
  3. PS图片去水印的方法
  4. 湖北武汉电信机房详细介绍
  5. 广东2022年养老金计发基数为8682元,人们都能领这样多养老金吗
  6. 黑苹果双系统安装教程macOS High Sierra 10.13.x
  7. 让你快速掌握技巧,新手怎么做自媒体?分享6点干货知识
  8. GPU图形处理器与CPU区别
  9. scum官方服务器维护时间,在等公测的这段时间里聊聊最近很火的scum吧
  10. 福利彩票36选7模拟程序