题目链接:PTA | 程序设计类实验辅助教学平台
D.Find the Number
样例输入:
5
38 47
57 86
23 24
72 83
32 33
样例输出:
-1
68
-1
-1
-1
题意:定义一个好数:二进制表示中1的个数和后缀0的个数相同的数。
每次询问给出一组[l,r],输出该区间内的任意一个好数,如果没有就输出-1.
这道题我用两种方法来进行分析:
我的第一种思路就是枚举后缀0的个数,然后采取贪心构造的方法,让r逐渐减少来进行构造。我们从r的高位为1的地方开始找,找到第一个与l二进制中不同的数位,然后就可以从这一位开始进行判断,但是在找不同的数位的过程中由于相同数位的值已经被确定,所以我们需要统计过程中已经确定的1的个数,假如要枚举后缀0个数为5的情况,那么我们就知道倒数第六位一定是1,那么我们的目标就是在除了前六位的其他数位上放置4个1,我们先去构造那些我们不能抉择的数位,也就是从高位开始r和l数位上的数值相同的数位,因为我们要构造的数是介于l和r之间的,所以这些数位本质上是已经确定的了,假如我们已经找到了第id位l和r上二进制位是不同的,在找到这一位之前我们已经使用了一个1,那么我们就需要在id~7之间放置3个1,这个时候我们贪心来放置,假如我们先在id位放置一个1,这个时候我们构造的数还是有可能大于r的,但是一定是大于l的,这个我们只需要最后判断一下即可,然后为了使得我们构造的数尽可能小,所以我们选择在低数位8~7这两个位置放置两个1,然后我们比较一下我们当前构造的数和r的关系即可,如果当前这个数是小于等于r的,那么这个数就是符合题意的一个数,直接输出即可。如果大于r,那么也就是说我们不可能在第id位放置1,这个时候我们尝试在第id-1位放置1,这个时候我们构造的数一定是小于r的,为了尽可能使得我们构造的数是大于等于l的,我们尝试直接在高三位id-1~id-3上放置三个1,这是我们能构造的最大的满足题意的数,如果是大于等于l,那么这也是一个符合题意的解,如果上述两种情况都不能构造成功,则一定无解。
细节见代码:
#include
#include
#include
#include
#include
下面说一下第二种思路:
一开始这道题我是想着打表去做,然后写了个暴力发现1~1e9之间的好数大概有5e5个,显然是放不下的,所以就放弃了这个思路。但是我们可以直接在运行时爆搜出所有的好数并存储在vector中,然后对于每组询问我们直接二分答案即可。
细节见代码:
#include
#include
#include
#include
#include
样例输入:
7 7
1000011
1 3
2 4
3 5
4 6
5 7
1 6
2 7
样例输出:
0
1
1
0
0
1
1
题意:给定一个长度为n的01串,每次询问给定一个区间[l,r](区间长度是3的倍数),我们把该区间内的字符串取出来首尾相接形成环,然后对于每个1,我们可以将其和其相邻的两个字符一块消除,我们可以进行一种操作,就是每次操作就是选定一个0将其变成1,问我们至少需要操作多少次才能将该环内的字符全部消除。
分析:首先我们能够发现,最少的操作次数也就是等价于在不进行操作的前提下用1来尽可能地消除0,最后剩余的0的最小数目除以3就是我们的最少操作次数。现在我们就转化为求解如何利用已有的1来尽可能多地消除0.通过简单的模拟一些小的数据可以发现,如果两个1连在一起,那么他们只能整体进行消除,也就是说每个1的贡献都是0.5(我们这里的贡献就是消除0的个数),而单独的一个1就可以消除两个0,也就是贡献是2,由此我们就可以得出一个结论,连续的k个1的贡献和k的奇偶性有关,如果k是偶数,那么贡献就是k/,否则就是(k-1)/2+2,这样我们就可以求出来连续1的贡献了,这时候大家可能会产生这样一个疑问,就是说比如我一个奇数个1和奇数个1之间原来有0相隔,但是把0消掉之后连到一起反而使得1的贡献降低,怎么避免这种情况呢?其实大家可以这么想,在一开始我们把连续的0都尽可能地消至1个0,那么这个时候我们就是用单独的0把一些连续的1间隔了,那么这个时候我们一定可以把0全部消除,对应的策略就是我们的连续的1先用来消除0多的那一块,最后使得两端的0都为1个即可。分析到这我们就知道了怎么知道一个环能不能消除完所有的0,但是我们不可能对于每一次询问都遍历一遍求取连续的1的贡献,所以我们可以预处理出连续的1的贡献,我们能够发现对于一个询问只有询问两端的1可能连起来,而中间部分的1的贡献我们都可以使用前缀和预处理,所以对于每次询问我们只需要统计一下两端的1连起来的贡献即可。这个我们可以提前预处理一下0的位置,这样就可以O(1)统计贡献了。
细节见代码:
#include
#include
#include
#include
#include
L LCS-like Problem
样例输入:
apple
grape
样例输出:
3
题意:给定一个s串和t串,问s串中满足其子序列与t串的最长公共子序列长度小于等于1的最长子序列的长度。
分析:首先我们先来看一下t串的作用是什么,它能够告诉我们哪些字符是不能前后存在的,拿样例来说,t串为grape,首先ge这个序列是不能存在的,有位ge这个序列和t串的最长公共子序列长度是大于1的,所以我们就可以预处理出来哪两个字符是不能先后存在的,为什么是两个呢?因为多个字符是以两个字符为基础的,两个字符都不存在更不可能存在多个字符。这一部分复杂度是O(26*n),然后我们设f[i][j]表示匹配到s串的第i个字符以字符j结尾的满足题意的序列的最大长度,接下来我们进行状态转移方程的推导:
我们来讨论一下s串的第i个字符,如果s串的第i个字符没有在t串中存在,那么对于所有的0<=j<26我们显然有f[i][j]=f[i-1][j]+1,这个地方需要注意一下,就是我们状态明明设置的以字符j结尾的情况的最大长度,为什么添加一个t中不存在的字符后他会使得所有满足题意的序列长度+1?我们可以这样理解,既然当前字符在t中没有出现过,那么我放在哪根本不会产生影响,我们可以先考虑将其放在了字符j的前面(当然这样理解是有违背子序列的含义的,一会我会说这样做的原因),下面来看看s串的第i个字符在t串中出现过的情况,那么我们就枚举可以放在当前字符前面的所有字符,然后令f[i][s[i]]=max(f[i][s[i]],f[i][j]+1)即可,也就是需要满足vis[j][s[i]]=true的条件。需要注意的一点是我们对于更新i时一定要继承i-1的所有方案,这个是大部分动态规划都需要做的事情,这里就不赘述了。这个时候可能会产生一个疑问:就是我们放置当前字符s[i]时只考虑了他前面一个字符,但是并没有考虑在前一个字符之前的字符啊,比如是abc万一要是出现a和b不矛盾,b和c不矛盾但是a和c矛盾怎么办?其实这个是不可能出现的,假设a,b,c在s串和t串中均出现过,否则可以直接放不需要讨论,首先我们看一下a和b不矛盾是属于什么情况,说明在t串中b是出现在字符a之前的,而b和c不矛盾则说明c是出现在字符b之前的,那么我们就能得到字符a是出现在c之前的,所以不可能会有矛盾。这也就是我们结尾的字符的含义是特指s串和t串公共字符的原因,否则这里会出现错误,简单模拟一下就知道错误在哪了。
细节见代码:
#include
#include
#include
#include
#include