1585 例题1 Amount of Degrees(Ural 1057 LOJ10163) 进制转换纯枚举从40分到60分 数位DP100分 初次使用string

news/2024/11/25 13:44:40/

总目录

在线测评地址(ybt)

在线测评地址(LOJ)

看完题目,感觉就是一个进制转换。

样例数据如下:

17对应2进制10001 数1的个数有2个
18对应2进制10010 数1的个数有2个
20对应2进制10100 数1的个数有2个

该题有个极端的测试数据:

1 2^31-1
20
2即
1 2147483647
20
2

很明显,AC的解法,一定是成片的计算数量。

目前能怎么办?从初级的算法入手,慢慢改进,看能到什么程度。

1.进制转换,纯枚举,输出符合的数量.

样例通过,提交40分

ybt

未通过

测试点结果内存时间
测试点1答案正确620KB2MS
测试点2答案错误616KB2MS
测试点3运行超时580KB995MS
测试点4运行超时596KB998MS
测试点5运行超时580KB998MS
测试点6答案正确612KB63MS
测试点7运行超时588KB1000MS
测试点8运行超时580KB998MS
测试点9运行超时580KB998MS
测试点10运行超时584KB998MS

LOJ

 40分代码如下:

#include <bits/stdc++.h>
using namespace std;
int x,y,k,b;
void init(){scanf("%d%d%d%d",&x,&y,&k,&b);
}
int count1(int a){int cnt=0;while(a){cnt+=((a%b)==1);//统计1的数量 a/=b;if(cnt>k){//优化,避免无谓的计算 cnt=-1;break;}}return cnt;
}
void solve(){int i,ans=0;for(i=x;i<=y;i++){ans+=(count1(i)==k);}printf("%d\n",ans);
}
int main(){init();solve();return 0;
}

超时可以理解,有测试点错误,不可接受,想不通。

突然想到一组测试数据:

输入:
1 9
1 3输出:
3有效的是
1=1*3^0
3=1*3^1
9=1*3^2

然而上述代码对应的输出是5

很显然5=2*3^0+1*3^1等无效数据也被统计了。

ybt

未通过

测试点结果内存时间
测试点1答案正确608KB2MS
测试点2答案正确612KB2MS
测试点3运行超时576KB998MS
测试点4运行超时584KB998MS
测试点5运行超时580KB998MS
测试点6答案正确616KB92MS
测试点7运行超时576KB996MS
测试点8运行超时576KB998MS
测试点9运行超时572KB998MS
测试点10运行超时572KB997MS

LOJ

 60分代码如下:

#include <bits/stdc++.h>
using namespace std;
int x,y,k,b;
void init(){scanf("%d%d%d%d",&x,&y,&k,&b);
}
int count1(int a){int cnt=0;while(a){if(a%b==1)cnt+=1;//统计1的数量else if(a%b){//系数不是1 cnt=-1;break;} a/=b;if(cnt>k){//优化,避免无谓的计算 cnt=-1;break;}}return cnt;
}
void solve(){int i,ans=0;for(i=x;i<=y;i++){ans+=(count1(i)==k);}printf("%d\n",ans);
}
int main(){init();solve();return 0;
}

2.数位DP​​​​​​

由于所求的幂互不相等,符合条件的数写成B进制一定只由01组成。

因此我们只考虑B=2的情况。

此处区间满足区间减法,即count(X,Y)=count(0,Y)-count(0,X-1)

因此我们唯一需要求的就是,给定n,求出所有不超过n的正整数中,其二进制表示恰好含有K"1"的有多少。

这里,我使用一棵完全二叉树来代表一个区间内的数。

例如图中高度为4的完全二叉树就可以代表所有长度为4的二进制数。

(左枝是0,右枝是1,有点像哈夫曼编码。)

其范围为[0,2^4-1]

每个叶子就代表了一个数。

假设K=3 n=13,其二进制为1101

为了方便起见,树的根( 人为引入 )用 0 表示。这样,这棵高度为 4 的完全二叉树就可以表示所有 4
位二进制数( 0..2^ 4 -1 ),每一个叶子节点代表一个数。其中,红色路径表示 n 。所有小于 n
数组成了三棵子树,分别用蓝色、绿色、紫色表示。因此,统计小于 13 的数,就只需统计
这三棵完整的完全二叉树:统计蓝子树内含 3 1 的数的个数、统计绿子树内含 2 1 的数
的个数(因为从根到此处的路径上已经有 1 1 ),以及统计紫子树内含 1 1 的数的个数。
注意到,只要是高度相同的子树统计结果一定相同。而需要统计的子树都是“右转”时遇到
的( 请注意:统计的全是左子树 )。当然,我们不能忘记统计 n 本身。实际上,在算法最初时将 n 自加 1 ,可以避免讨论 n
本身,但是需要注意防止上溢。

很容易递推求出这个问题:

f[h,s]表示在高度为h(h>=1,因根节点0是人为引入)的完全二叉树包含的数中(范围是[0,2^h-1]),二进制中恰含s个1数有多少。(注意s<=h,因具体到某个数,是从根节点走到叶节点经历的01)

f[h,s]  =  f[h-1,s] (左子树中的个数) +  f[h-1,s-1](右子树中的个数)

(注:f[h,s]根是0

f[h-1,s]左子树的根是0,故除左子树根节点0外,还需s个1.

f[h-1,s-1]右子树的根是1,故除右子树根节点1外,还需s-1个1.)

剩下的问题就是,如何将任意进制问题转化为二进制问题。

(若左起有大于1的数位,下面转换对右边界Y没有问题,转换只是将不大于Y的符合题意的数找出,因Y本身不符合题意,故右边界没有少计数,也没有多计数。)

(若左起有大于1的数位,下面转换对左边界X没有问题,转换只是将不大于X的符合题意的数找出,因X本身不符合题意,故左边界没有少计数,也没有多计数。)

(思维难点)只需贪心将nB进制表示转化为只含01:找到n的左起(左是高位)第一位大于1的数位,将它以及它右边所有数位赋为1(即找到最接近n的符合题意的数)

递推求f的时间复杂度为O(log^2(n)),每次询问的时间复杂度为O(log(n)),因此总时间复杂度为O(log^2(n))

问题得到完美解决!

实际上,我们也可以只用“位”的思想来考虑这个问题,而不使用树的思想。

我们每次找到n"1"数位,将它赋为0,则其右面的数位可任取01

可根据组合数算出满足题意的数的个数。

穷举所有“1”数位,计算组合数,即可完成询问。

事实上,f的递推公式正是组合数公式!

与树的思想异曲同工!

 使用高度为h的完全B叉树表示所有长度为hB进制数,思考起来更加直观,在处理较复杂问题时优点明显。

当然,最终程序我们并不真正建树,它的作用只是帮助我们思考。

无论从树结构考虑还是从数位的角度考虑,基本思想都是:

 逐位确定

ybt

通过

测试点结果内存时间
测试点1答案正确600KB2MS
测试点2答案正确604KB1MS
测试点3答案正确604KB1MS
测试点4答案正确616KB1MS
测试点5答案正确600KB1MS
测试点6答案正确608KB1MS
测试点7答案正确604KB1MS
测试点8答案正确600KB1MS
测试点9答案正确600KB1MS
测试点10答案正确612KB1MS

LOJ

#include <bits/stdc++.h>
using namespace std;
int x,y,k,b;
int f[35][35];
void init(){scanf("%d%d%d%d",&x,&y,&k,&b);
}
int i2b(int x){//将整数x转换成B进制 string s;int i,j;while(x){//将整数x转换成字符串,逆序存储 s=s+char(x%b+'0');//注意 2<=B<=10,故s%b的值落在[1,9]区间 x/=b;}for(i=0;i<s.size();i++){if(s[i]>'1')//若i位大于1,将0-i位之间数据设置为1 for(j=0;j<=i;j++)s[j]='1';}x=0;for(i=0;i<s.size();i++)//以01形式重写x x|=((s[i]-'0')<<i);return x;
}
void fx(){//处理手法,类杨辉三角 int i,j;//全是0,全是1,均只有一种情形,初始化,需注意for(i=0;i<=31;i++)//边界初始化 f[i][0]=f[i][i]=1; for(i=1;i<=31;i++)for(j=1;j<i;j++)f[i][j]=f[i-1][j]+f[i-1][j-1];
}
int count(int x){int i,j,tot=0,ans=0;for(i=31;i>0;i--){//自高位向低位扫描 if(x&(1<<i)){tot++;if(tot>k)break;x^=(1<<i);//将第i位从1置为0 }if((1<<(i-1))<=x)//寻找合适的左子树 ans+=f[i-1][k-tot];}if(tot+x==k)ans++;//判断自身是否相同return ans; 
}
int main(){init();x=i2b(x-1);y=i2b(y);fx();printf("%d\n",count(y)-count(x));return 0;
}

该题习得什么?

第一次对数位DP有了印象,心心念的二进制位操作出现了。

在统计ans过程中,细节较多,该题若作为试题,100分的代码初次编写时很难完成的。


http://www.ppmy.cn/news/790779.html

相关文章

UVA - 1585 Score

Description There is an objective test result such as “OOXXOXXOOO”. An ‘O’ means a correct answer of a problem and an ‘X’ means a wrong answer. The score of each problem of this test is calculated by itself and its just previous consecutive ‘O’s on…

装修知识笔记

文章目录 衣柜要不要到顶?办公柜 改水电布局家具吊顶吊顶种类吊顶架子 隔墙 门如何安装门电视背景墙和电视柜悬空电视柜 采光卫生间防水和下水 非专业专修人员&#xff0c;只是喜爱&#xff0c;毕竟多懂一点&#xff0c;自己的家就有机会舒适一些。 衣柜 为什么放到最前面&am…

以阻塞方式对IO文件进行读取

以阻塞方式对IO文件进行读取(test.c读取&#xff0c;test2.c发送数据) 实验结果 执行test.c生成的pro1可执行文件&#xff0c;光标显示处于阻塞状态 执行test2.c生成的pro2可执行文件&#xff0c;test.c处打印 hello dhl 三级标题test.c #include <stdlib.h> #inclu…

十三亿人都能看懂的导航栏吸顶+tab

设计思路 要想出现滚动效果,就需要让里面的内容高度大于外面内容的高度\ <body> <div class"box"> <div class"top"> <img src"../img/ad.jpg" alt""> </div> <…

不破不立~EDG夺冠,用Python分析词云图展示粉丝弹幕数据,来感受粉丝的热情吧

大家好~我是恰恰&#xff0c;好久不见啦~Python的乐趣就在于在互联网时代&#xff0c;能实现很多人工做不到的事~ 虽然我不是经常玩游戏&#xff0c;但是我这该死的爱国情怀&#xff0c;在EDG夺冠的时候&#xff0c;我也是十分激动的&#xff01; 北京时间11月6日&#xff0…

iMeta | 南昌大学丁霞等-水产养殖系统对中华鳖微生物组和肠道代谢组的影响

点击蓝字 关注我们 水产养殖模式对水产动物皮肤、口腔和肠道微生物群落组装及宿主适应性的影响 https://doi.org/10.1002/imt2.17 4.5 iMeta RESEARCH ARTICLE ● 2022年4月5日&#xff0c;南昌大学丁霞等在iMeta在线发表题为“The impact of aquaculture system on the microb…

lol服务器位置地图,LOL老玩家一定能看懂的地图 每一个地点都充满回忆

英雄联盟赛事发展至今&#xff0c;已经从最初的小打小闹&#xff0c;发展到现如今的全球性的大型盛会。而每年的比赛上都会留下令人津津乐道的招牌操作&#xff0c;这些操作或搞笑或惊艳。 而近日&#xff0c;就有一位能人就将这些最经典的操作用梗的形融合在了召唤师峡谷地图上…

AndroidStudio一键国际化方案

预研 国际化对于只做国内市场的小伙伴来说基本没有太多感觉&#xff0c;但是对于做国外市场特别是谷歌市场的朋友来说却是需要重视的一个知识点。因为海外市场面对的全球的客户&#xff0c;而如果人工翻译势必很费时费力而且低效&#xff0c;这时候我们需要程序化来实现这个体…