3.5、算法练习
一、自由练习
1)资源限制
时间限制:1.0s 内存限制:512.0MB
2)问题描述
求出区间[a,b]中所有整数的质因数分解。
3)输入格式
输入两个整数a,b。
4)输出格式
每行输出一个数的分解,形如k=a1a2a3…(a1<=a2<=a3…,k也是从小到大的)(具体可看样例)
5)样例输入
3 10
6)样例输出
3=3
4=22
5=5
6=23
7=7
8=222
9=33
10=25
7)数据规模和约定
2<=a<=b<=10000
8)解题思路
1.若为质数,直接输出;不是质数,分解为质数相乘。所以先判断质数——isPrime函数判断是否为质数
2.调用isPrime函数,并用for循环,将2—b之间的质数保存。在a—b间,判断质数并输出;若不为质数,temp=i,暂存i,用temp除以v[intex],能整除,则是所找的质数,并判断是否输出“ * ”。
提示:先筛出所有素数,然后再分解。
#include<iostream>
#include<vector>
using namespace std;
bool isPrime(int n)
{
int i;
for(i=2;i<=n/2;i++){
if(n%i == 0){
return false;
}
}
if(i>n/2){
return true;
}
else{
return false;
}
}
int main()
{
int a,b;
cin>>a>>b;
vector<int> v;
for(int i=2;i<=b;i++){
if(isPrime(i)){
v.push_back(i);
}
}
for(int i=a;i<=b;i++)
{
if (isPrime(i)){//若i为质数,直接输出
cout<<i<<"="<<i;
}
else{ //i不为质数
cout<<i<<"=";
int temp=i;
int index=0;
while(temp != 1)
{
if(temp%v[index] == 0)
{
cout << v[index];
temp /= v[index];
index = 0;
if (temp != 1){
cout<<"*";
}
}
else{
index++;
}
}
}
cout<<endl;
}
return 0;
}
二、视频学习
1)视频链接
https://www.bilibili.com/video/BV1jE411g76D?p=21
2)动态规划知识点
动态规划(dynamic programming)与分治法类似,都是通过组合子问题求解原问题。(在这里,programming 指表格法。而非“编程”)。分治法是将原问题分为互不相交的子问题,递归的求解子问题,再将它们组合起来,求出原问题的解。与之相反,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题,动态规划算法对每个子子问题只求解一次,将其解保存在表格中,从而不用每次求解该子子问题时都要反复计算。
总结来讲:分治法的子子问题是全新的,动态规划的子子问题是有重叠的。
使用动态规划必须满足的2条性质:
- 最优子结构性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
- 重叠子问题:递归算法会反复求解相同的子问题,而不是一直生成新的子问题,(对比分治法:递归的每一步都生成全新的子问题),动态规划算法能够最每个子问题都只求解一次,将解存表中,需要时直接查表。但是应该注意,同一问题的其中一个子问题的解不能影响另一个子问题的解,即无关。换句话说,当前问题的每一次划分生成的子问题之间不存在重复的资源。
动态规划算法求解的基本步骤如下:
(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
(2)确定状态和状态变量:注意状态必须满足无后效性。
(3)确定决策:找到子问题是进行动态规划的重要一步。动态规划和递推更多应考虑本问题由哪些已解决子问题构成,而不是考虑本问题对将来哪些问题有贡献。
(4)确定边界条件,写出状态转移方程。
(5)代码编程
求解子问题的最优解
动态规划的实现有2种等价的方法:1.带备忘的自顶向下(递归)
仍按原始的递归形式,只是在递归求解过程中记录每个子问题的解,存储到数组(通常在使用前会初始化值,常用正负无穷)中。当需要一个子问题的解时,首先检查是否保存过此解,如果保存过(非初始值),则直接返回该解,节省计算时间;否则,按常规方式计算该子问题的解并记录。2.自底向上
4)问题描述
Sereja在平面上画了n个点,点i在坐标(i,0)。然后,Sereja给每个点标上了一个小写或大写英文字母。Sereja不喜欢字母"x",所以他不用它标记点。Sereja认为这些点是漂亮的,当且仅当:
·所有的点可以被分成若干对,使得每个点恰好属于一一对之中。
·在每对点中,横坐标较小的会被标上小写字母,较大的会被标上对应的大写字母。
·如果我们在每对点上画一个正方形,其中已知的一对点会作为正方形的相对的顶点,它们间的线段会成为正方形的对角线,那么在所有画出的正方形中不会有相交或触碰的情况。
小Petya擦掉了一些小写字母和所有大写字母,现在Sereja想知道有多少种方法来还原每个点上的字母,使得还原后这些点是漂亮的。
5)输入格式
第一行是一个整数n,表示点的个数。
第二行是一个长度为n的字符串,包含小写字母和问号"?",是按照横坐标递增的顺序的每个点的描述。问号表示这个点的字母被Petya擦掉了。保证输入串不含字母"x"。
6)输出格式
输出答案对4294967296取模的值。如果没有可行的方案,输出0。
7)样例输入
4
a???
8)样例输出
50
9)样例输入
4
abc?
10)样例输出
0
11)样例输入
6
abc???
12)样例输出
1
13)数据规模和约定
20个测试点的n分别为:5,10,20,50,100,200,500,1000,2000,5000,10000,20000,30000,40000,50000,60000,70000,80000,90000,100000.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define PP pair<ll,int>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3fll
#define dinf 1000000000000.0
#define PI 3.1415926
#define LL unsigned int
typedef long long ll;
using namespace std;
int n,p;
LL f[200010]={1},jg=1;
char c[100010];
int main() {
cin>>n;
cin>>c+1;
if(n&1)
cout<<"0"<<endl;
else{
int m=n>>1;
for(int i=1;i<=n;++i){
if(c[i]=='?')
for(int j=i>>1;j>=i-m&&j;j--)
f[j]+=f[j-1];
else
p++;
}
for(int i=1;i<=m-p;i++)
jg=jg*25;
jg=(jg*f[m]);
cout<<(LL)jg<<endl;
}
return 0;
}