最近做模拟题看到一些好的题及题解。
升格思想:
核电站问题
一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。
任务:对于给定的N和M,求不发生爆炸的放置核物质的方案总数
输入:输入文件只一行,两个正整数N,M( 1<N<50,2≤M≤5)
输出:输出文件只有一个正整数S,表示方案总数。
运用升格思想。设N个坑不会发生爆炸的方案数是f[N],那么我们假设N以前的坑的方案
都已知了,那么我们只需要考虑第N个坑如何放即可(顺序是从左到右)。我们考虑两种情况:
1、当N这个坑放入物质,那么有两种可能,一种是因为之前放了连续的m-1个,现在放了就会爆炸,那么这种情况有f[N-1-m]种情况,这是要剪掉的。一种是不会发生爆炸,那么方案数就是f[N-1]。所以这个坑放入物质的方案有f[N-1]-f[N-1-m]。
2、当N这个坑不放入物质,那么方案直接就是f[N-1]。
所以f[N]=f[N-1]+f[N-1]-f[N-1-m]
其中f[0]=f[-1]=1
动态规划:最优化问题第一应该要想到动归
鹰蛋实验
在ural大学的一个教授的别墅上有一鹰巢。教授对这个鹰巢很感兴趣。经过仔细观察,他发现鹰巢中有若干枚蛋。于是他想利用这些蛋做一个试验。测试一下蛋的坚固程度。
这些蛋应该是具有相同的坚硬度。存在一个非负整数E,如果从楼的第E层往下扔蛋,但不会破,但如果从第E+1层(包括高于E+1层)扔,蛋就会破。你要做一组试验,来找出E。最简单的方法是一层层试。但是你有多个蛋试,不必用笨方法,可以用更少的次数找出E。注意这里的次数都是指对你的方法的最坏情况且蛋破了就不能再用,还有E可以取0。
如果实验到了最高层蛋还不破,则认为E取最高层的层数。
输入:一行,蛋的个数n和楼的层数n,k<=1000。(中间一个空格)
输出:最少实验次数。
第一次试验有10种选择——1到10层
如果选第3层,最坏情况下当然E!=3,如果E<3,蛋就碎了(关键!),那么还需要a[2][1]次
如果E>3,那么还需要a[7][2]次,由于考虑最坏情况,取两个的最大值,再加1
如果n层楼,m个蛋,其实就是
for(i=1;i<=n;i++) a[n][m]=min{1+max{a[i-1][m-1],a[n-i][m]}}
当然还要注意剪枝。
1000层楼,最多需要10个蛋
木棒分组
【题目描述】
有 n 根木棒,你需要将它们分成尽量多的组,满足每组木棒长度和相等。你不能将木棒砍断。输出最优方案下一组中木棒长度之和。
【输入格式】
第一行一个整数 n。接下来 n 个整数,表示 n 根木棒的长度
【输出格式】
一行一个整数,表示木棒长度之和
【样例输入】
4
2 5 3 4
【样例输出】
7
【数据范围】
70%的数据保证 n<=8
100%的数据保证 n<=50
这题数据很小我们可以考虑枚举组数(显然<=50,且能整除sigma(a[i])),然后因为对于组量,我们不需要考虑顺序,只需要考虑是否能够有足够的元素分成一组即可(但是要用最少的元素),所以我们用01背包来dfs,每一次就判断是否能在剩余的元素中找尽量少的元素匹配到sum/组数这个值。一定要注意初始化,因为这个问题我调试了老半天。。
拓展欧几里得是y-=a/b*x啊啊啊啊。不是y-=x*a/b啊啊啊,是取下界!!!!!
寂寞的程序员
【题目描述】
有两个寂寞的程序员 J 和 Y,J 说他可以两分钟敲 8000 字,Y 说不信,于是 J 与 Y的 4kb/min 机器人进行了一场打字比赛。机器人以匀高速敲着乱码,而 J 找到了帮他超越手速的键:CTRL。J 可以花不同的时间进行以下操作:
1. 敲一个字符
2. 按 CTRL-A
3. 按 CTRL-C
4. 按 CTRL-V
(具体效果请在记事本内实验)
你需要制定一个操作顺序,使得 J 在 n 毫秒内输入的字符数最多。
【输入文件】
第一行五个整数 n cost input cost CtrlA cost CtrlC cost CtrlV ,四个cost分别对应进行四个操作需要耗费的毫秒数
【输出文件】
一行一个整数,表示能够输入的最多字符数
【样例输入】
14 10 1 1 1
【样例输出】
2
【数据范围】
30%的数据保证 n ≤ 10
100%的数据保证 1 ≤ n ≤ 1,000; 1 ≤ cost ≤ n
这题的dp我一开始就想错了QAQsad。。我们只需要设状态d[i]表示i时刻最多能打的字,然后d[i]=max(d[i-ctrla-ctrlc-ctrlv-j*ctrlv]*(j+1))即可。。这个自己多想想吧,很容易的。所以以后我们看到诸种类型的题,即一个操作对其它的操作有依赖,那么直接将这些操作看做整体来做(但是要从头到尾扫一遍更新)
带*号的公共字串匹配
有串A和B,其中A中带*号的字符表示这个地方可赋值任意串(包括空串),问是否可以匹配A和B
设状态d[i,j]表示A中前i和B中前j是否能被匹配,则
d[1,0]=true 当 A[1]=’*’
d[0,0]=true
d[i,j]=d[i-1,j-1], A[i]==B[j]
d[i,j]=d[i-1,j-1] || d[i-1,j] || d[i,j-1], A[i]!=B[j]
状态压缩:
驴友
有一批驴友在深山中探险,他们来到了一个山洞的洞口。所有驴友都必须穿过这个山洞。可是山洞太窄,他们无法同时穿过山洞。因此,他们只能分成小组分别穿越山洞。然而,他们只有一个手电筒。因此一组人穿越山洞后,必须有人带着手电筒回来,直到最后一个人穿越山洞。
山洞中有座桥,每次穿越山洞的人必须同时过桥。然而,桥的承重量有限,不能承受超过m的质量,否则桥会垮掉。每个人的体重事先已经知道了。可以有单独一个驴友穿越山洞。当然,如果同时穿越山洞的小组人数多于一人,则每个人都必须至少要有另外一个他信任的人也在这个小组中。给定一个邻接矩阵T,T[i,j]等于”Y”则表示i信任j,否则i不信任j。每个人行走的速度不尽相同,因此一组人走的时候,按照其中走的最慢的人的速度行进。你已经知道了每个人分别穿越山洞所需的时间。请你求出,所有人都通过山洞所需的最小时间。如果不可能穿越山洞,输出-1。
【输入文件】
第一行两个整数n,m,表示总人数和桥的承重量。
接下来一行有n个整数,表示每个人的质量。接下来一行有n个整数,表示每个人通过山洞所需时间。
接下来是一个n×n的字符矩阵,只有‘Y’和‘N’两种字符,表示信任矩阵。
【输出文件】
一个整数,表示最小时间,戒者-1 表示无解。
【数据范围】
1 ≤ n ≤ 13 , 1 ≤ m ≤ 5000
看到n<=13就应该要想到状压。我们考虑两种状态,状态一和状态二。都表示当前人的情况。只不过状态一是表示还没进洞,状态二表示已经进洞。那么状态一转移到状态二显然就是两个状态的交这些人进了洞(反过来也行)。所以我们预处理所有的两边的可能情况,然后算出每种情况的最小费用,那么就在这些状态上连边。最后从没进洞的状态(所有人)跑一遍最短路,答案就是进洞的状态(所有人)。
以后看到数据范围<=15,可以优先想状压。
最短路:
逃生
小 J 在一次旅行中感染了一只可以分裂细菌,这种细菌在出现一分钟后会分裂成两只相同重量的细菌,其中一只不再分裂,另一只在一分钟后继续分裂。小 J 想尽快找到杀菌草药,然而可供行走的路并不多。山里共有 n 个水池与 m 条路,每条路连接两个水池,走过这条道路需要花费一定的时间。小 J 感染时在 s 号水池,杀菌草药生长在 t 号水池里。小 J 每经过一个水池时都要喝一口水来补充体力,但是细菌也会产生变化:现存所有细菌都会合体为一只,重量为所有细菌重量之和,合体一分钟后继续分裂。然而,小 J 并不知道这一点,每经过一个水池,他依然会去喝水。你需要为小 J 画出一条从 s 号水池到 t 号水池的路线,使得小 J 到达 t 号水池时身上的细菌重量最小。数据保证有且只有一组解。
【输入格式】
第一行四个整数 n m s t
接下来 m 行,每行三个整数 l r w,表示 l 号水池与 r 号水池间有一条道路,花费为 w 分钟(喝水不花费时间)。同一对 l r 之间可能有多条道路
【输出格式】
第一行一个整数 k,表示路线长度
接下来一行 k 个整数,依次表示你画出的路线中经过的每个点
【样例输入】
4 4 1 4
1 2 0
1 3 2
3 4 1
2 4 4
【样例输出】
3
1 2 4
【数据范围】
30%的数据保证 n ≤ 50; w ≤ 10
100%的数据保证 2 ≤ n ≤ 10000; 1 ≤ m ≤ 30000; 0 ≤ w ≤ 1000; s ≠ t; l ≠ r
很显然一下就能转换成 d[v]<=d[u]+d[u]*w[u, v]的三角不等式。但是对于这种数据,显然是会爆表的。。。。。我们考虑这样,
d[v]<=d[u]*(1+w[u, v]) ->
log(d[v])<=log(d[u]*(1+w[u,v])) ->
log(d[v])<=log(d[u])+log(1+w[u,v])
那么我们直接将边权变成log然后相加就行了
分数规划:
越狱
【题目描述】
某*有一排 n 个连续的房间,每个房间有硬度不一的铁门,也关着个数不一的囚犯。一天,这些囚犯为了实现梦想而想筹划越狱,他们准备让一段连续的房间里的人冲出铁门控制狱卒,成功的可能性定义为暴动人数/铁门硬度和。为了分散狱卒注意力,暴动房间数不能少于 L。你作为狱卒里的内奸,需要制定一个成功的可能性最大的方案。若有多组解,输出任意一种。
【输入格式】
第一行两个整数 n L
第二行 n 个整数,第 i 个整数a i 表示第 i 个房间里的人数
第三行 n 个整数,第 i 个整数b i 表示第 i 个房间的铁门的硬度
【输出格式】
一行两个整数 s t,表示让s~t(包含 s,t)号房间的人暴动。
【样例输入】
3 2
1 2 3
3 2 1
【样例输出】
2 3
【数据范围】
30%的数据保证n ≤ 1,000100%的数据保证1 ≤ L ≤ n ≤ 100,000; 1 ≤ a i , b i ≤ 10,000
其实一开始看到除法我就想到了分数规划,但是不知道如何计算f(t)>=0。。。。
其实很简单。。。。我们处理好所有a[i]-d*b[i]后,只需要找>=L的区间的最大值即可。。。
而且O(n)就可以解决。。。就是维护一个前缀和,只需要每次维护一个最小前缀和(根据sum[r]-sum[l-1]要最大嘛)然后在维护最大值。然后不要写错check啊。。。我的check的返回值是bool然后在二分里判断check是<-eps。。。。Sad。。
说明区间给定长度求最值可以用前缀和实现O(n)
分数规划的话,还是老实自己推:
设B=sigma(a[i]*x[i])/sigma(b[i]*x[i])的b是最值(即最优解),这里的x[i]表示第i个是否选择,且a[i]和b[i]都是已知的。
我们设F(A)=sigma(a[i]*x[i])-A*sigma(b[i]*x[i])
当F(A)>0时(假设现在是求最大值B),我们发现
sigma(a[i]*x[i])-A*sigma(b[i]*x[i])>0 即存在一种方案X使得
sigma(a[i]*x[i])/sigma(b[i]*x[i])>A,那么显然这时左式比A要优,那么A式就可以直接使用它(说明对于A来说,有更优的解,在二分里边的解释就是,F(mid)>0那么l=mid,即逼近下界)
我们再看一下F的变形
F(A)=sigma(a[i]*x[i])-A*sigma(b[i]*x[i])
=sigma((a[i]-A*b[i])*x[i])
显然a[i]-A*b[i]是可以直接求出来的,那么问题转化为求出一种方案X使得F(A)>0,
最小值的话同理
数论:
有限小数
【题目描述】
小 J 是一个严谨的人,他只能接受有限小数,而总有些数令他不爽,比如0.3 。一天,他发现0.3 在 3 进制下可以写成 0.1,在 30 进制下可以写成 0.A 等(A(30) = 10(10))。于是他坚信所有有理数都可以写成有限小数。然而,不断切换进制是很麻烦的工作,现在小 J 需要处理若干有理数,你需要告诉他最少需要几进制才能使它们都能写成有限小数。
【输入格式】
第一行一个整数 n。接下来 n 行,每行两个十进制整数 a b,表示一个分数
a
b
【输出格式】
一行一个十六进制整数,表示最小进制数
【样例输入】
2
1 33
1 99
【样例输出】
21
【数据范围】
100%的数据保证n ≤ 1,000; 1 ≤ a, b ≤ 100,000,000
设r = p1*p2*..*pk (pi为互不相等的质数)
设B = {b|b=p1^t1*p2^t2*..*pk^tk (ti>=0)}
充分性:对于任意b∈B,a/b可以在r进制下写成有限小数
必要性:对于任意c∉B,既约分数a/c不能在r进制下写成有限小数
printf(“%X”)的%X通配符可以直接输出16进制数。
计数:计数的话不是组合计数就是递推计数,注意好递推计数的抽象!
错排公式
当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用D(n)表示,那么D(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
第二步,放编号为k的元素,这时有两种情况:
⑴把它放到位置n,那么,对于剩下的n-1个元素,由于第k个元素放到了位置n,剩下n-2个元素就有D(n-2)种方法;
⑵第k个元素不把它放到位置n,这时,对于这n-1个元素,有D(n-1)种方法;
综上得到
D(n) = (n-1) [D(n-2) + D(n-1)]
特殊地,D(1) = 0, D(2) = 1.
Stirling数
第一类Stirling数是有正负的,其绝对值是n个元素的项目分作k个环排列的方法数目。常用的表示方法有s(n,k),[nk]。
换个较生活化的说法,就是有n个人分成k组,每组内再按特定顺序围圈的分组方法的数目。例如s(4,2):
{A,B},{C,D}
{A,C},{B,D}
{A,D},{B,C}
{A},{B,C,D}
{A},{B,D,C}
{B},{A,C,D}
{B},{A,D,C}
{C},{A,B,D}
{C},{A,D,B}
{D},{A,B,C}
{D},{A,C,B}
这可以用有向图来表示。
给定s(n,0)=0,s(1,1)=1,有递归关系s(n+1,k)=s(n,k−1)+ns(n,k)
递推关系的说明:考虑第n+1个物品,n+1可以单独构成一个非空循环排列,这样前n种物品构成k-1个非空循环排列,方法数为s(n,k-1);也可以前n种物品构成k个非空循环排列,而第n+1个物品插入第i个物品的左边,这有n*s(n,k)种方法。
即s(n,k)=s(n-1,k-1)+(n-1)*s(n-1,k)
第二类Stirling数是n个元素的集定义k个等价类的方法数目。常用的表示方法有S(n,k),S(k)n,{nk}。
换个较生活化的说法,就是有n个人分成k组的分组方法的数目。例如有甲、乙、丙、丁四人,若所有人分成1组,只有所有人在同一组这个方法,因此S(4,1)=1;若所有人分成4组,只可以人人独立一组,因此S(4,4)=1;若分成2组,可以是甲乙一组、丙丁一组,或甲丙一组、乙丁一组,或甲丁一组、乙丙一组,或其中三人同一组另一人独立一组,即是:
{A,B},{C,D}
{A,C},{B,D}
{A,D},{B,C}
{A},{B,C,D}
{B},{A,C,D}
{C},{A,B,D}
{D},{A,B,C}
因此S(4,2)=7。
给定S(n,n)=S(n,1)=1,有递归关系S(n,k)=S(n−1,k−1)+k*S(n−1,k)
递推关系的说明:考虑第n个物品,n可以单独构成一个非空集合,此时前n-1个物品构成k-1个非空的不可辨别的集合, 方法数为S(n-1,k-1);也可以前n-1种物品构成k个非空的不可辨别的 集合,第n个物品放入任意一个中,这样有k*S(n-1,k)种方法。