02枚举和计数练习题解析

枚举和计数练习题及参考代码

01统计好三元组

问题描述

给你一个整数数组 arr ,以及 abc 三个整数。请你统计其中好三元组的数量。

如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。

  • 0 <= i < j < k < arr.length
  • |arr[i] - arr[j]| <= a
  • |arr[j] - arr[k]| <= b
  • |arr[i] - arr[k]| <= c

其中 |x| 表示 x 的绝对值。

返回好三元组的数量

  • 3 <= arr.length <= 100
  • 0 <= arr[i] <= 1000
  • 0 <= a, b, c <= 1000

输入描述

第一行输入一个整数n

第二行是n个由空格分割的整数,代表数组arr

第三行是3个整数分别代表 abc

输出描述

输出一个整数代表好三元组的数量

输入样例

6

3 0 1 1 9 7

7 2 3

输出样例

4

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n,a,b,c;
	
	cin>>n;
    vector<int> arr(n);
	for(int i=0;i<n;i++){
		cin>>arr[i];
	} 
	cin>>a>>b>>c;
	
	int result = 0;
	for(int i=0;i<n-2;i++){
		for(int j=i+1;j<n-1;j++){
			for(int k=j+1;k<n;k++){
				if( abs(arr[i]-arr[j])<=a && abs(arr[j]-arr[k])<=b && abs(arr[i]-arr[k])<=c) result++;
			}
		}
	}
	
	cout<<result;
	return 0;
}

02蛇形数组

问题描述

蛇形填充方法为:
对于每一条左下-右上的斜线,从左上到右下依次编号 按编号从小到大的顺序,将数字从小到大填入各条斜线,其中编号为奇数的从左下向右上填写,编号为偶数的从右上到左下填写。

在这里插入图片描述

现在,给你一个整数 n ,请问它在蛇形数组的第几行第几列?

  • n ≤ 1 0 8 n \le 10^8 n108

输入描述

输入一个整数n

输出描述

输出n所在的行列x y

输入样例

6

输出样例

1 3

参考代码

#include<iostream> 

using namespace std;

int main(){
	int n;
	cin>>n;
	
	int i=1,x=1,y=1,st=1,ch=0;
	while(i<n){
		i++;
		if(x==1 && ch==0){
			y++;
			st=1;
			ch=1;
			continue;
		}
		if(y==1 && ch==0){
			x++;
			st=-1;
			ch=1;
			continue;
		}
		x += st;
		y -= st;
		ch=0;
	}
	cout<<x<<' '<<y<<endl;
	return 0;
}

03手机信号

问题描述

已知你身边的信号塔位置 towers 和一个整数 radius 代表信号塔的信号覆盖范围。

towers 中信号塔表示为 towers[i] = [xi, yi, qi] 表示第 i 个网络信号塔的坐标是 (xi, yi) 且信号强度参数为 qi 。所有坐标都是在 X-Y 坐标系内的 整数 坐标。两个坐标之间的距离用 欧几里得距离 计算。

信号塔的信号覆盖范围 radius 表示一个塔能到达的 最远距离 。如果一个坐标跟塔的距离在 radius 以内(包含radius),那么该塔的信号可以到达该坐标。在这个范围以外信号会很微弱,所以 radius 以外的距离该塔是 不能到达的

如果第 i 个塔能到达 (x, y) ,那么该塔在此处的信号为 ⌊qi / (1 + d)⌋ ,其中 d 是塔跟此坐标的距离。一个坐标的 信号强度 是所有 能到达 该坐标的塔的信号强度之和。

请你返回数组 [cx, cy] ,表示 信号强度 最大的 整数 坐标点 (cx, cy) 。如果有多个坐标网络信号一样大,请你返回字典序最小的 非负 坐标。

注意:

  • 坐标 (x1, y1) 字典序比另一个坐标 (x2, y2) 小,需满足以下条件之一:

    • 要么 x1 < x2
    • 要么 x1 == x2y1 < y2
  • ⌊val⌋ 表示小于等于 val 的最大整数(向下取整函数)。

  • 1 <= towers.length <= 50

  • towers[i].length == 3

  • 0 <= xi, yi, qi <= 50

  • 1 <= radius <= 50

输入描述

第一行一个整数 n 代表信号塔的个数,一个整数 r 代表信号塔的信号覆盖范围

其后 n 行,每行三个整数,分别代表 xi, yi, qi

输出描述

输出一个整数坐标cx cy

输入样例

3 2

1 2 5

2 1 7

3 1 9

输出样例

2 1

参考代码

#include<iostream>
#include<vector>
#include<math.h>

using namespace std;

int main(){
	int n,r,minx=50,miny=50,maxx=0,maxy=0,maxr=0,resx,resy;
	cin>>n>>r;
	vector<vector<int>> towers(n,vector<int>(3,0));
	for(int i=0;i<n;i++){
		cin>>towers[i][0]>>towers[i][1]>>towers[i][2];
		minx = min(minx, towers[i][0]);
		maxx = max(maxx, towers[i][0]);
		miny = min(miny, towers[i][1]);
		maxy = max(maxy, towers[i][1]);
	}
	
	for(int i=minx;i<=maxx;i++){
		for(int j=miny;j<=maxy;j++){
			int ra = 0;
			for(int k=0;k<n;k++){
				int dis = sqrt((towers[k][0] - i)*(towers[k][0] - i) + (towers[k][1] - j)*(towers[k][1] - j));
				if(dis<=r){
					ra += towers[k][2] / (1+dis);
				}
			}
			if(ra>maxr){
				maxr = ra;
				resx = i;
				resy = j;
			}
		}
	}
	
	cout<<resx<<' '<<resy;
	return 0;
}

04修剪灌木

问题描述

爱丽丝要完成一项修剪灌木的工作。

n 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晚会修剪一棵灌木,让灌木的高度变为0厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始,每天向右修剪一棵灌木。当修剪了最右侧的灌木后,她会调转方向,下一天开始向左修剪灌木。直到修剪了最左的灌木后再次调转方向。然后如此循环往复。

灌木每天从早上到傍晚会长高1厘米,而其余时间不会长高。在第一天的早晨,所有灌木的高度都是0厘米。爱丽丝想知道每棵灌木最高长到多高。

  • 1 < n ≤ 10000 1<n\le10000 1<n10000

输入描述

一个正整数 n 代表灌木的个数

输出描述

输出 n 行,每行一个整数,第 i 行表示从左到右第 i 棵树最高能长到多高。

输入样例

3

输出样例

4

2

4

参考代码

#include<iostream>

using namespace std;

int main()
{
	int n;
	cin>>n;
	
	for(int i=1;i<=n;i++)
	{
		cout<<max(i*2-2,(n-i+1)*2-2)<<endl;
	}
	
	return 0;
}

05连续整数求和

问题描述

给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数

例如:

输入: n = 5
输出: 2
解释: 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5。
  • 1 < = n < = 1 0 9 1 <= n <= 10^9 1<=n<=109

输入描述

一个整数n

输出描述

输出一个整数

输入样例

5

输出样例

2

参考代码

#include<iostream>
#include<vector>

using namespace std;

bool isKConsecutive(int n, int k) {
        if (k % 2 == 1) {
            return n % k == 0;
        } else {
            return n % k != 0 && 2 * n % k == 0;
        }
    }

int main(){
	int n;
	cin>>n;
	
	int result=0;
	for(int k = 1; k * (k + 1) <= 2*n; k++) {
        if (isKConsecutive(n, k)) {
            result++;
        }
    }
    
    cout<<result;
    return 0;
}

如果正整数 n n n 可以表示成 k k k 个连续正整数之和,则由于 k k k 个连续正整数之和的最小值是 ∑ i = 1 k i = k ( k + 1 ) 2 \sum_{i = 1}^k i = \frac{k(k + 1)}{2} i=1ki=2k(k+1) ,因此有 n ≥ k ( k + 1 ) 2 n \ge \frac{k(k + 1)}{2} n2k(k+1) ,即 k ( k + 1 ) ≤ 2 n k(k + 1) \le 2n k(k+1)2n。枚举每个符合 k ( k + 1 ) ≤ 2 n k(k + 1) \le 2n k(k+1)2n 的正整数 k k k,判断正整数 n n n 是否可以表示成 k k k 个连续正整数之和。

如果正整数 n n n 可以表示成 k k k 个连续正整数之和,假设这 k k k 个连续正整数中的最小正整数是 x x x,最大正整数是 y y y,则有 y = x + k − 1 y = x + k - 1 y=x+k1,根据等差数列求和公式有 n = k ( x + y ) 2 = k ( 2 x + k − 1 ) 2 n = \frac{k(x + y)}{2} = \frac{k(2x + k - 1)}{2} n=2k(x+y)=2k(2x+k1) x = n k − k − 1 2 x = \frac{n}{k} - \frac{k - 1}{2} x=kn2k1 ,根据 k ( k + 1 ) ≤ 2 n k(k + 1) \le 2n k(k+1)2n 可知 x > 0 x > 0 x>0。分别考虑 k k k 是奇数和偶数的情况。

  • k k k 是奇数时, k − 1 k - 1 k1 是偶数,因此 2 x + k − 1 2x + k - 1 2x+k1 是正偶数。令 q = 2 x + k − 1 2 q = \frac{2x + k - 1}{2} q=22x+k1 ,则 qq 是正整数, n = k q n = kq n=kq q = n k q = \frac{n}{k} q=kn 。由于 q q q 是正整数,因此 n n n 可以被 k k k 整除。
    n n n 可以被 k k k 整除时,由于 $\frac{n}{k} $ 和 k − 1 2 \frac{k - 1}{2} 2k1 都是整数,因此 x = n k − k − 1 2 x = \frac{n}{k} - \frac{k - 1}{2} x=kn2k1 是整数。又由于 x > 0 x > 0 x>0,因此 x x x 是正整数。因此 n n n 可以表示成 k k k 个连续正整数之和。
    综上所述,当 k k k 是奇数时,「正整数 n n n 可以表示成 k k k 个连续正整数之和」等价于「正整数 n n n 可以被 k k k 整除」。

  • k k k 是偶数时,2x + k - 12x+k−1 是奇数。将 n = k ( 2 x + k − 1 ) 2 n = \frac{k(2x + k - 1)}{2} n=2k(2x+k1) 写成 $\frac{2x + k - 1}{2} = \frac{n}{k} $ ,由于 2x + k - 12x+k−1 是奇数,因此 $\frac{2x + k - 1}{2} $ 不是整数, n n n 不可以被 k k k 整除,又由于 2 x + k − 1 = 2 n k 2x + k - 1 = \frac{2n}{k} 2x+k1=k2n是整数,因此 2n2n 可以被 k k k 整除。
    n n n 不可以被 k k k 整除且 2n2n 可以被 k k k 整除时,$\frac{2n}{k} $ 一定是奇数(否则 $\frac{n}{k} $是整数,和 n n n 不可以被 k k k 整除矛盾),令 $\frac{2n}{k} = 2t + 1 $,其中 t t t 是整数,则 $\frac{n}{k} = t + \frac{1}{2} $。此时 x = n k − k − 1 2 = t + 1 2 − k 2 + 1 2 = t − k 2 + 1 x = \frac{n}{k} - \frac{k - 1}{2} = t + \frac{1}{2} - \frac{k}{2} + \frac{1}{2} = t - \frac{k}{2} + 1 x=kn2k1=t+212k+21=t2k+1,由于$ \frac{k}{2} $ 是整数,因此 x x x 是整数。又由于 x > 0 x > 0 x>0,因此 x x x 是正整数。因此 n n n 可以表示成 k k k 个连续正整数之和。
    综上所述,当 k k k 是偶数时,「正整数 n n n 可以表示成 k k k 个连续正整数之和」等价于「正整数 n n n 不可以被 k k k 整除且正整数 2 n 2n 2n 可以被 k k k 整除」。

根据上述分析,可以得到判断正整数 n n n 是否可以表示成 k k k 个连续正整数之和的方法:

  • 如果 $k $是奇数,则当 n n n 可以被 k k k 整除时,正整数 n n n 可以表示成 k k k 个连续正整数之和;

  • 如果 k k k 是偶数,则当 n n n 不可以被 k k k 整除且 2 n 2n 2n 可以被 k k k 整除时,正整数 n n n 可以表示成 k k k 个连续正整数之和。

06号码牌

问题描述

给你从0到1的号码牌各 n 张,你需要用这些号码牌从1开始拼出正整数,每拼一个就保存起来,号码牌就不能用来拼其他的数了,请问你可以拼到多少?

例如:

当你有30张号码牌,其中0到9各3张,则你可以拼出1到10,但是拼11时号码牌已经只有一张了,不够拼出11.

  • 1 < = n < = 1 0 7 1 <= n <= 10^7 1<=n<=107

输入描述

一个整数n

输出描述

输出一个整数

输入样例

3

输出样例

10

参考代码

#include <iostream>
#include <vector>

using namespace std;

vector<int> num;

bool check(int i)
{
    while(i)
    {
        if(--num[i%10]<0){return false;}
        i /= 10;
    }
    return true;
}

int main()
{
	int n;
	cin>>n;
	num = vector<int>(10,n);
	
    int i=1;
    while(check(i)) i++; 
    
    cout<<--i;
    return 0;
}

07第一个唯一字符

问题描述

给你一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1

  • 1 ≤ s . l e n g t h ≤ 1 0 5 1 \le s.length \le 10^5 1s.length105
  • s 只包含小写字母

输入描述

一个字符串 s

输出描述

输出一个整数

输入样例

aabb

输出样例

-1

参考代码

#include<iostream>
#include<unordered_map>
#include<string> 

using namespace std;

int main()
{
	string s;
	unordered_map<char,int> map;
	cin>>s;
	for(char c:s){
		map[c]++;
	}
	
	for(int i=0;i<s.length();i++){
		if(map[s[i]]==1){
			cout<<i;
			return 0;
		}
	}

	cout<<-1;
	return 0;
}

08数青蛙

问题描述

给你一个字符串 s,它表示不同青蛙发出的蛙鸣声(字符串 croak )的组合。由于同一时间可以有多只青蛙呱呱作响,所以s 中会混合多个 croak

请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。

要想发出蛙鸣 croak,青蛙必须 依序 输出 ‘c’, ’r’, ’o’, ’a’, ’k’ 这 5 个字母。如果没有输出全部五个字母,那么它就不会发出声音。如果字符串 s 不是由若干有效的 croak 字符混合而成,请返回 -1 。

示例 1:

输入:croakcroak
输出:1
解释:一只青蛙 “呱呱” 两次

示例 2:

输入:crcoakroak
输出:2
解释:最少需要两只青蛙,“呱呱” 声用黑体标注
第一只青蛙 “crcoakroak”
第二只青蛙 “crcoakroak

示例 3:

输入:croakcrook
输出:-1
解释:给出的字符串不是 “croak” 的有效组合。

  • 1 <= croakOfFrogs.length <= 105
  • 字符串中的字符只有 'c', 'r', 'o', 'a' 或者 'k'

输入描述

一个字符串 s

输出描述

输出所有蛙鸣所需不同青蛙的最少数目

输入样例

croakcroak

输出样例

1

参考代码

#include<iostream>
#include<unordered_map>
#include<string> 

using namespace std;

int main()
{
	string s;
	cin>>s;
	int c=0;
    int r=0;
    int o=0;
    int a=0;
    int k=0;
    int re=0;
    bool flag=true;
    for(int i=0; i<s.size(); i++){
        if (s[i]=='c') c++;
        if (s[i]=='r') r++;
        if (s[i]=='o') o++;
        if (s[i]=='a') a++;
        re=max(re, c);//遇到k前要判断有多少个c同时存在
        if (s[i]=='k'){//遇到k就要规约一个croak
            k++;
            if (c>=r && r>=o && o>=a && a>=k){
            c--;
            r--;
            o--;
            a--;
            k--;
            }
                
        }
        if(!(c>=r && r>=o && o>=a && a>=k)){//必须保持任意时刻(c>=r>=o>=a>=k),才是正确的;否则就是错误的,
            flag=false;
            break;
        }
            
    }
    if (c!=0 || r!=0 || o!=0 || a!=0 ||k!=0) flag=false;//如果最后有剩的字母,也是错误的
    if (flag==true) cout<< re;
    else cout<<-1;
    
    return 0;
}	

09数组对是否可以被 k 整除

问题描述

给你一个整数数组 arr和一个整数 k ,其中数组长度是偶数,值为 n

现在需要把数组恰好分成 n / 2 对,以使每对数字的和都能够被 k 整除。

如果存在这样的分法,请返回 true ;否则,返回 false

  • arr.length == n
  • ` 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105
  • n 为偶数
  • − 1 0 9 ≤ a r r [ i ] ≤ 1 0 9 -10^9 \le arr[i] \le 10^9 109arr[i]109
  • 1 ≤ k ≤ 1 0 5 1 \le k \le 10^5 1k105

输入描述

第一行一个整数 n表示数组的长度,一个整数k

第二行n个由空格分割的整数,表示数组的内容。

输出描述

如果存在这样的分法,请返回 true ;否则,返回 false

输入样例

6 7

1 2 3 4 5 6

输出样例

true

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n,k;
	vector<int> arr;
	vector<int> mod;
	cin>>n>>k;
	arr = vector<int>(n,0);
	mod = vector<int>(k,0);
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	// 统计余数为0到k-1的个数
    for (int num: arr) {
        ++mod[(num % k + k) % k];
    }
    // 余数为i与余数为k-i个数不相等则不能配对
    for (int i = 1; i + i < k; ++i) {
        if (mod[i] != mod[k - i]) {
            cout<<"false";
            return 0;
        }
    }
    // 余数为0的个数必须为偶数
    if( mod[0] % 2 != 0 ){
    	cout<<"false";
        return 0;
	}
	
	cout<<"true";
	return 0;
}

10序列中不同最大公约数的数目

问题描述

给你一个由正整数组成的数组 nums

数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。

例如,序列 [4,6,16] 的最大公约数是 2
数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。

例如,[2,5,10][1,2,1,2,4,1,5,10] 的一个子序列。
计算并返回 nums 的所有 非空 子序列中 不同 最大公约数的 数目

在这里插入图片描述

示例 1:

输入:nums = [6,10,3]
输出:5
解释:上图显示了所有的非空子序列与各自的最大公约数。
不同的最大公约数为 6 、10 、3 、2 和 1 。

示例 2:

输入:nums = [5,15,40,5,6]
输出:7

  • 1 ≤ n u m s . l e n g t h ≤ 1 0 5 1 \le nums.length \le 10^5 1nums.length105
  • 1 ≤ n u m s [ i ] ≤ 2 ∗ 1 0 5 1 \le nums[i] \le 2 * 10^5 1nums[i]2105

输入描述

第一行一个整数 n表示数组的长度

第二行n个由空格分割的整数,表示数组的内容。

输出描述

如果存在这样的分法,请返回 true ;否则,返回 false

输入样例

3

6 10 3

输出样例

5

参考代码

#include<iostream>
#include<vector>

using namespace std;

int gcd(int a, int b){
	if(b){
		return gcd(b, a%b);
	}
	return a;
}

int main(){
	int n,c=0;
	cin>>n;
	vector<int> nums(n,0);
	
	for(int i=0;i<n;i++){
		cin>>nums[i];
		c = max(c, nums[i]);
	}
	
	vector<int> g(c + 1);
	
	for (int x: nums) {
        for (int y = 1; y * y <= x; ++y) {
            if (x % y == 0) {
                if (!g[y]) {
                    g[y] = x;
                }
                else {
                    g[y] = gcd(g[y], x);
                }
                if (y * y != x) {
                    int z = x / y;
                    if (!g[z]) {
                        g[z] = x;
                    }
                    else {
                        g[z] = gcd(g[z], x);
                    }
                }
            }
        }
    }
        
    int ans = 0;
    for (int i = 1; i <= c; ++i) {
        if (g[i] == i) {
            ++ans;
        }
    }
    cout<<ans;
	return 0;
}

假设最大的数是maxnum,总体思路就是遍历[1,maxnum],检查每一个值x是否可能是一个序列的最大公约数。

如何检查呢?显然,这个序列中的每一个数都是x的倍数,这是一个形如[1x,2x...nx] (nx<=maxnum)的序列。但这只是必要条件,并不是充分条件。比如x=2,序列[4,6,8]的最大公约数是2,而序列[4,8]的最大公约数是4。我们发现,这个序列不仅是x的倍数,还可以是x的某个倍数的倍数。于此同时,我们知道最大公约数的一个重要性质gcd(a,b,c)=gcd(gcd(a,b),c),这告诉我们,序列中加入的数越多,最大公约数只会越小。当我们把数组中所有x的倍数都加入了序列,如果求得的最大公约数是x,那就说明x是一个解了。如果此时求得的最大公约数仍然是x的某个倍数,就说明x不能构成任何一个序列的最大公约数。

分解每一个数的约数,这样能够直接得到x的所有倍数集合,然后求每个集合的最大公约数。
在这里,divisor[4]=[1,2,4], divisor[6]=[1,2,3,6]divisor[8]=[1,2,4,8], 所以:

g[1]=[4,6,8], gcd(4,6,8) = 2 != 1
g[2]=[4,6,8], gcd(4,6,8) = 2 == 2
g[3]=[6], gcd(6) = 6 != 3
g[4]=[4,8], gcd(4,8) = 4 == 4
g[5]=[]
g[6]=[6], gcd(6) = 6 == 6
g[7]=[]
g[8]=[8], gcd(8) = 8 == 8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuelanghanbao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值