TSOJ 好好做题(屑)——递推状态压缩+高精度
文章目录
- 题目描述
- 解题思路
- 喜闻乐见的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 好好做题(屑)——递推状态压缩+高精度相关推荐
- BZOJ-4300 绝世好(蛋疼)题 DP(递推)
翻zky学长的blog时翻出来的..... 4300: 绝世好题 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 736 Solved: 393 [Sub ...
- hdu 1297 递推难题
这题的话,我能玩一年 今天做了很多递推的题,这题无疑是最复杂的 其实可以看出来,2,3,4,5为一类,不妨定义为2型,1,6为一类,定义为1型 规定num[i]为结尾是i的凹槽的数量 我们可以能轻易的 ...
- 递推算法与递推套路(手撕算法篇)
联系我们:有道技术团队助手:ydtech01 / 邮箱:[ydtech@rd.netease.com] 之前学习基础知识的时候也说了,递推和动态规划有这暧昧不清的关系,可以说,动态规划就是多了一个决策 ...
- 递推算法与递推套路(算法基础篇)
联系我们:有道技术团队助手:ydtech01 / 邮箱:[ydtech@rd.netease.com] 相信了解算法同学经常会说动态规划太难了,看到题目完全不知从何下手,或者是说"一看题解就 ...
- P1759 通天之潜水(不详细,勿看)(动态规划递推,组合背包,洛谷)
题目链接:点击进入 题目分析: 简单的组合背包模板题,但是递推的同时要刷新这种情况使用了哪些物品 ac代码: #include<bits/stdc++.h> using namespace ...
- 震惊!递推与递归竟然可以这么编!%99的程序员都不知道!
四.归并排序(逆序对) (一).归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全 ...
- C++奥赛一本通递推题解
title: C++奥赛一本通刷题记录(递推) date: 2017-11-08 tags: 一本通 openjudege categories: OI C++奥赛一本通刷题记录(递推) 2017.1 ...
- 怎么判断第几范式例题_学完就忘、做题就懵!初级会计怎么备考才能更高效?...
备考初级会计之路真是艰辛,目前官方政策初级会计职称是一年一考,早早开始学吧,又有惰性,总觉得时间还很多,报完名再开始学吧,又总是搞不透彻知识点,一个知识点需要一天甚至更多才能消化...... 很多时候 ...
- 【转】状态压缩动态规划
引入 首先来说说"状态压缩动态规划"这个名称,顾名思义,状态压缩动态规划这个算法包括两个特点,第一是"状态压缩",第二是"动态规划". 状 ...
最新文章
- 即将到截止日期,Frontiers期刊向您邀稿啦!脑机接口领域
- 在servlet中设置的字符编码集为什么还会出现乱码(亲测)
- java继承上机作业
- 可编程交换时代就在这里
- 华为交换机默认vlan都是通的吗_华为设备二层交换技术——Hybrid接口详解
- phpmyadmin的安装和使用
- 谷歌地图自定义popup框
- 中信银行Java笔试题库,手撕面试官
- SOUI知识点小结2
- O2O电子商务盈利模式是什么 O2O电子商务模式遇到的困难有哪些?
- Centos版Linux 一些常用操作命令 收集
- Selenium显示等待和隐式等待
- vue 总结一项目建立及文件夹结构配置
- python操作xlsx格式文件
- 【玖哥乱弹】神通广大的JavaScript
- MultipartFile和File互转
- 触动传媒总部人去楼空 上海多家出租车公司追讨欠款
- 日本美术学校有哪些,日本美术学校排名榜
- textpad java sdk_如何防止TextPad在运行Java时创建临时批处理文件?
- Android 刷机/Root/安装Xposed