zoukankan      html  css  js  c++  java
  • 「基础算法」第1章 递推算法强化训练

    「基础算法」第1章 递推算法强化训练

    A. 1.划分数列

    题目

    题目描述

    给定一个长度为 n 的数列 A ,要求划分最少的段数,使得每一段要么单调不降,要么单调不升。

    输入格式

    第一行一个整数 n 。

    接下来 n 个数表示数列 A 。

    输出格式

    输出最少的划分数。

    样例

    样例输入 1

    6
    1 2 3 2 2 1
    

    样例输出 1

    2
    

    样例输入 2

    9
    1 2 1 2 1 2 1 2 1 
    

    样例输出 2

    5
    

    样例输入 3

    7
    1 2 3 2 1 999999999 1000000000
    

    样例输出 3

    3
    

    数据范围与提示

    [1leq n leq1e5,a_i在int范围内 ]

    思路

    挺简单的一道题:

    (up_i)表示数列A中,([up_i,i])这一段区间单调不下降,(down_i)表示数列A中,([down_i,i])这一段区间单调不上升,(f_i)表示([1,i])这一段区间内最小的合法划分段数.

    显然(难道不显然吗),有递推式:

    [f_i=min egin{cases} f_{up_{i}-1}+1\ f_{down_{i}-1}+1 end{cases} ]

    代码

    #include <iostream>
    #include <cstdio>
    #define nn 100010
    using namespace std;
    int read() {
    	int re = 0 , sig = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig * re;
    	
    }
    int f[nn];
    int a[nn];
    int up[nn] , down[nn];
    int n;
    #define min_(_ , __) (_ < __ ? _ : __)
    int main() {
    	up[0] = down[0] = 1;
    	n = read();
    	for(int i = 1 ; i <= n ; i++) {
    		a[i] = read();
    		if(a[i] == a[i - 1] || i == 1)
    			up[i] = up[i - 1] , down[i] = down[i - 1];
    		else
    			if(a[i] > a[i - 1])
    				up[i] = up[i  - 1] , down[i] = i;
    			else
    				up[i] = i , down[i] = down[i - 1];
    	}
    	
    	for(int i = 1 ; i <= n ; i++)
    		f[i] = min_(f[up[i] - 1] , f[down[i] - 1]) + 1;
    	cout << f[n] << endl;
    	return 0;
    }
    

    B. 2.求 f 函数

    题意

    给定函数

    [f(x)= egin{cases} f(f(x+11)) &xleq100\ x-10 &xgeq101 end{cases} ]

    输入若干个x((xin[0,1e6])),(以0结束),O(1)求出(f(x))

    其实数据水的很,直接暴力也能过

    思路

    看到当(xleq100)时才需要递归,就毫不犹豫的把x=1~100的表打了出来,结果惊人的一幕出现了,输出的全是91

    所以就有了以下代码:

    int read() {//快读(主函数外)
    	int re = 0 , sig = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig * re;
    	
    }
    //(主函数内)
    while(x = read()) printf("%d
    " , x <= 100 ? 91 : x - 10);
    
    

    结果,A了……

    但问题是为什么呢?

    从x=101开始,多推几次:

    f(101)=91
    f(100)=f(f(111))=f(101)=91
    f(99)=f(f(110))=f(100)=91
    f(98)=f(f(109))=f(9)=91
    f(x)=f(x+1)=91
    ...
    f(91)=91
    f(90)=f(f(101))=f(91)=91
    f(89)=f(f(100))=f(91)=91
    ...
    

    这样,就得到了上面的结论,这道题也完美地解决了

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int read() {
    	int re = 0 , sig = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig * re;
    	
    }
    int f(int x) {
    	return x <= 100 ? f(f(x + 11)) : x - 10;
    }
    int x;
    int main() {
    	while(x = read()) printf("%d
    " , x <= 100 ? 91 : x - 10);
    	return 0;
    	//以下用于打表
    	for(int i = 1 ; i <= 100 ; i++)
    		cout << f(i) << " , ";
    	while(x = read())printf("%d
    " , f(x));
    	return 0;
    }
    

    C. 3.无限序列

    题目

    题目描述

    我们按以下方式产生序列:

    1. 开始时序列是: 1
    2. 每一次变化把序列中的 1 变成 100 变成 1

    经过无限次变化,我们得到序列 1011010110110101101 ...

    总共有 Q 个询问,每次询问为:在区间 a 和 b 之间有多少个 1

    任务:写一个程序回答 Q 个询问。

    输入格式

    输入的第一行为一个整数 Q ,后面有 Q 行,每行两个数用空格隔开的整数 a , b 。

    输出格式

    输出共 Q 行,每行一个回答。

    样例

    样例输入

    1
    2 8
    

    样例输出

    4
    

    数据范围与提示

    [1leq Q leq5000,1leq a leq b <2^{63} ]

    思路

    依旧是找规律:

    第1次变化(初始):1
    第2次变化:10
    第3次变化:101
    第4次变化:10110
    第5次变化:10110101
    
    

    用程序多弄几次,可以发现:若用(s(i))表示第i次变化后的结果,则有(s(i)=s(i-1)+s(i-2) (igeq 3))

    因此,若用(len(x))表示(s(i))的长度,(f(i))表示(s(i))中"1"的数量,则显然有:

    [f(x)= egin{cases} f(x-1)+f(x-2) &xgeq 3 \ 1 &x=1\ 1 &x=2 end{cases} \ len(x)= egin{cases} len(x-1)+len(x-2) &xgeq 3 \ 1 &x=1\ 2 &x=2 end{cases} ]

    不难无限次变化后1~x位中"1"的个数为:

    ll calc(ll x) {
    	if(x == 0)return 0;//注意特判,否则RE
    	for(int i = 1 ; i <= n ; i++) {
            //n=93,(len(n)刚好超过2^63而又不爆unsigned long long)
            //这里是为了枚举一个i,使f(i)>=x&&f(i-1)<x,其实可以二分,但是时间复杂度要求不高
    		if(len[i] == x)return f[i];//x刚好等于s(i)的长度,直接返回即可
    		if(len[i] > x) {
    			--i;//为了方便
    			return f[i] + calc(x - len[i]);//因为s(i)=s(i-1)+s(i-2),所以直接累加f(i-1)并向下递归即可
    		}
    	}
    }
    

    ([a,b])内"1"的数量就是:calc(b)-calc(a-1),就做完啦

    由于a最小为1,a-1=0,所以calc()中要特判x==0的情况

    代码

    #include <iostream>
    #include <cstdio>
    #define ll unsigned long long
    #define n 93
    using namespace std;
    ll len[110] , f[110];
    ll calc(ll x) {
    	if(x == 0)return 0;
    	for(int i = 1 ; i <= n ; i++) {
    		if(len[i] == x)return f[i];
    		if(len[i] > x) {
    			--i;
    			return f[i] + calc(x - len[i]);
    		}
    	}
    }
    int main() {
    	len[0] = 1 , len[1] = 1 , len[2] = 2;
    	f[0] = 0 , f[1] = 1 , f[2] = 1;
    	for(int i = 3 ; i <= n ; i++)
    		f[i] = f[i - 1] + f[i - 2],
    		len[i] = len[i - 1] + len[i - 2];
    	
    	int q;
    	cin >> q;
    	while(q--) {
    		ll a , b;
    		cin >> a >> b;
    		cout << calc(b) - calc(a - 1) << endl;
    	}
    	return 0;
    }
    
    /*
    10
    6 13
    21 81
    25 77
    9 37
    29 89
    48 97
    41 65
    65 85
    42 98
    17 73
    
    5
    38
    33
    18
    38
    31
    15
    13
    36
    35
    
    */
    

    D,E:未完,待续……

  • 相关阅读:
    linux 查看磁盘空间大小
    linux查看防火墙状态及开启关闭命令
    运行安装mysql 报错 [root@localhost mysql-mult]# ./scripts/mysql_install_db  --defaults-file=conf/3306my.cnf FATAL ERROR: please install the following Perl modules before executing ./scripts/mysql_install_
    linux lsof命令详解
    centos6下无法使用lsof命令"-bash: lsof: command not found"
    服务器创建好后怎样使用远程连接工具链接的一些问题
    mysql在linux下的安装
    jmeter-linux下运行
    【WPF】一组CheckBox的全选/全不选功能
    【WPF】TabControl垂直分页栏/选项卡
  • 原文地址:https://www.cnblogs.com/dream1024/p/14190703.html
Copyright © 2011-2022 走看看