2023XTU程序设计与实践刷题(练习二)

文章通过一系列编程题目展示了如何计算多个骰子投掷后数字和的概率,以及处理字符串表达式中字母出现次数、寻找最长合法区间和图像扫描路径等算法问题。这些问题涉及到递归、循环、排序和位运算等计算机科学基础知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

骰子

题目描述

你有n颗六面骰子,每个面上有一个数字,你随机地抛掷这n颗骰子,求各骰子顶面数字和的出现概率。

比如你有2颗骰子,那么它们的六面的数都是1∼6,那么出现的数字和是2∼12,其出现的概率依次为

输入格式

第一行是一个整数T (1≤T≤50),表示测试样例的个数。

以后每个样例的第一行是一个整数n(1≤n≤8),表示骰子的个数。 以后的n行,每行6个整数ai (1≤ai≤1024),表示第i个骰子六面上的数字。

输出格式

依次输出每个样例。

先输出会出现多少种数字;然后每行按出现数字和,从小到大输出,一个数字输出1行,格式如”n: x/y”,其中n为出现的数字,x/y为出现的概率分数,保证x,y互质。英文冒号后有一个空格。

样例输入

2

1

1 1 2 2 3 3

2

1 2 3 4 5 6

1 2 3 4 5 6

样例输出

3

1: 1/3

2: 1/3

3: 1/3

11

2: 1/36

3: 1/18

4: 1/12

5: 1/9

6: 5/36

7: 1/6

8: 5/36

9: 1/9

10: 1/12

11: 1/18

12: 1/36

心得与代码

这次整了个活,直接整了个八层嵌套循环,使用map输出就不用排序啦

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <map>


typedef __int64 ll;
using namespace std;
ll TZ[10][10];
ll fun(ll a,ll b){
	ll t;            		//求最大公约数 
	if(a<b){
		t=a;
		a=b;
		b=t;
	}
	while(b!=0){
		t=a%b;
		a=b;
		b=t;
	}
	return a;
}
int main(){
	ll N,k,n,sum,sum1,sum2,sum3,sum4,sum5,sum6,sum7;
	ll i,j,t,o,p,q,r,s;
	ll cnt,ans;
	map<ll,ll>::iterator iter;
	scanf("%I64d",&N);
	while(N--){
		map<ll,ll> map;
		scanf("%I64d\n",&n);
		memset(TZ,0,sizeof(TZ));
		for(i=0;i<n;i++){
			for(j=0;j<6;j++){
				scanf("%I64d",&t);
				TZ[i][j]=t;
			}
		}
		if(n==1){
			for(i=0;i<6;i++){
				sum=TZ[0][i];
				map[sum]++;
			}
		}
		else if(n==2){
			for(i=0;i<6;i++){
				sum=TZ[0][i];
				for(j=0;j<6;j++){
					sum1=TZ[1][j]+sum;
					map[sum1]++;
				}
			}
		}
		else if(n==3){
			for(i=0;i<6;i++){
				sum=TZ[0][i];
				for(j=0;j<6;j++){
					sum1=TZ[1][j];
					for(t=0;t<6;t++){
						sum2=TZ[2][t]+sum1+sum;
						map[sum2]++;
					}
				}
			}
		}
		else if(n==4){
				for(i=0;i<6;i++){
					sum=TZ[0][i];
					for(j=0;j<6;j++){
						sum1=TZ[1][j];
						for(t=0;t<6;t++){
							sum2=TZ[2][t];
							for(o=0;o<6;o++){
								sum3=TZ[3][o]+sum+sum1+sum2;
								map[sum3]++;
							}
						}
					}
				}
			}
		else if(n==5){
				for(i=0;i<6;i++){
					sum=TZ[0][i];
					for(j=0;j<6;j++){
						sum1=TZ[1][j];
						for(t=0;t<6;t++){
							sum2=TZ[2][t];
							for(o=0;o<6;o++){
								sum3=TZ[3][o];
								for(p=0;p<6;p++){
									sum4=TZ[4][p]+sum+sum1+sum2+sum3;
									map[sum4]++;
								}
							}
						}
					}
				}
			}
		else if(n==6){
				for(i=0;i<6;i++){
					sum=TZ[0][i];
					for(j=0;j<6;j++){
						sum1=TZ[1][j];
						for(t=0;t<6;t++){
							sum2=TZ[2][t];
							for(o=0;o<6;o++){
								sum3=TZ[3][o];
								for(p=0;p<6;p++){
									sum4=TZ[4][p];
									for(q=0;q<6;q++){
										sum5=TZ[5][q]+sum+sum1+sum2+sum3+sum4;
										map[sum5]++;
									}
								}
							}
						}
					}
				}
			}
		else if(n==7){
				for(i=0;i<6;i++){
					sum=TZ[0][i];
					for(j=0;j<6;j++){
						sum1=TZ[1][j];
						for(t=0;t<6;t++){
							sum2=TZ[2][t];
							for(o=0;o<6;o++){
								sum3=TZ[3][o];
								for(p=0;p<6;p++){
									sum4=TZ[4][p];
									for(q=0;q<6;q++){
										sum5=TZ[5][q];
										for(r=0;r<6;r++){
											sum6=TZ[6][r]+sum+sum1+sum2+sum3+sum4+sum5;
											map[sum6]++;
										}
									}
								}
							}
						}
					}
				}
			}
		else if(n==8){
				for(i=0;i<6;i++){
					sum=TZ[0][i];
					for(j=0;j<6;j++){
						sum1=TZ[1][j];
						for(t=0;t<6;t++){
							sum2=TZ[2][t];
							for(o=0;o<6;o++){
								sum3=TZ[3][o];
								for(p=0;p<6;p++){
									sum4=TZ[4][p];
									for(q=0;q<6;q++){
										sum5=TZ[5][q];
										for(r=0;r<6;r++){
											sum6=TZ[6][r];
											for(s=0;s<6;s++){
												sum7=TZ[7][s]+sum+sum1+sum2+sum3+sum4+sum5+sum6;
												map[sum7]++;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			cnt=0;
			ans=0;
		for( iter=map.begin();iter!=map.end();iter++){
					t=iter->second;
					if(t!=0) {
						cnt++;
						ans+=t;
					}
					
			}
			printf("%I64d\n",cnt);
			for( iter=map.begin();iter!=map.end();iter++){
				t=iter->second;
					if(t!=0) {
						s=iter->first;
						printf("%I64d: ",s);
						r=fun(t,ans);
						printf("%I64d/%I64d\n",t/r,ans/r);
					}
								
			}		
	}

} 

字母计数

题目描述

为了压缩一个只含小写英文字母的字符串,我们使用下面的方式表示它

  1. 任一字母c是表达式
  2. 任一字母c后接一个整数n也是一个表达式,表示把字母c重复n次,n是一个没有前导零的10进制整数,且 n≥2
  3. 如果s1,s2是表达式,那么s1s2也是表达式。
  4. S是一个表达式,那么(S)n也是表达式,表示将S这个表达式表示的字符串重复n次,n是一个没有前导零的10进制整数,且 n≥2

比如表达式 ((a2b)2b)2a 表示字符串aabaabbaabaabba

现在给你一个表达式,请统计一下其中各个字符出现的次数。

输入

存在多组样例,每样一个样例,为一个字符串。 字符串只含小写英文字母,数字和括号,其长度不超过50个字符,输入数据保证符合表达式要求,且展开后的字符出现次数最高不超过109

输出

依次输出每个样例的结果。 每个样例按照字母顺序,每个出现的字母输出一行。格式为"c : n",其中c为字母,n为出现的次数。 每个样例之后输出一个空行。

样例输入

((a2b)2b)2a

a2c3

样例输出

a : 9

b : 6

a : 2

c : 3

心得与代码

基本上就是照着题解思路写了,不过在写的时候还是遇到挺多问题的,比如宏观变量与局部变量的关系

后面和别的大佬讨论了下,还可以有从后面往前面读取的思路,也是一种挺妙的解法

思路:

dfs(s,e,c):
如果⾸字⺟是'(': // S -> (S)nS 形式
     找到匹配的右括号位置为i。
     dfs(s+1,i-1,c1)
     查找i后的数字串,数字串的结束位置为j。
     将其转换成数字t,并乘⼊数字c1。
 否则: // S -> cnS 或者 s -> cS 形式
     找到s+1开始的数字串,数字串结束位置为j。
     如果存在数字串,将其转换成数字t,否则t=1。
     c1中的s位置字符的数量为t,其他为0。
 dfs(j+1,e,c2)
 将c1和c2合并成c

#include <stdio.h>
#include <algorithm>
#include <string.h>
typedef __int64 ll;
using namespace std;
char str[100];
ll c3[30];

void dfs(int s,int e,ll c[]){
	
	ll c1[30],c2[30];
    memset(c1,0,sizeof(c1));
	memset(c2,0,sizeof(c2));
	int i,j,t,o,l=1,r=0;
	ll sum=0;
	if(str[s]=='('){
		l=1;
		r=0;
		for(i=s+1;i<=e;i++){
			if(str[i]=='(') l++;
			if(str[i]==')'){
				r++;
				if(r==l) 	break;
			
			}
		}
		dfs(s+1,i-1,c1);
//		ll sum=0;
		for(t=i+1;t<=e;t++){
			if(str[t]>='0'&&str[t]<='9'){
				sum+=str[t]-48;
				sum=sum*10; 
			}
			else break;
		}
		j=t-1;
		sum=sum/10;
		for(o=0;o<=26;o++){
			c1[o]*=sum;
		}
	}
	else {
		for(i=s+1;i<=e;i++){
			if(str[i]>='0'&&str[i]<='9') continue;
			else break;
		}
		j=i-1;
		if(i==s+1) sum=1;
		else{
			sum=0;
			for(o=s+1;o<=j;o++){
				sum+=str[o]-48;
				sum=sum*10; 
			}
			sum=sum/10;
		}
		c1[str[s]-'a']+=sum;
	}
	if(j+1<=e)dfs(j+1,e,c2);
	for(o=0;o<=26;o++){
		
		c[o]=c1[o]+c2[o];
	}
	for(o=0;o<=26;o++){
			
	c3[o]=c[o];
		
	}
}
int main(){
	ll i,j,cnt;
	memset(str,0,sizeof(str));
	memset(c3,0,sizeof(c3));

	while(scanf("%s",str)!=EOF){
		cnt=0;
		for(i=0;str[i]!='\0';i++){
			cnt++;
		}
		dfs(0,cnt-1,c3);
		for(i=0;i<=26;i++){
			if(c3[i]!=0){
				char ch=i+97;
				printf("%c : %I64d\n",ch,c3[i]);
			}
		}
		printf("\n");
		memset(str,0,sizeof(str));
		memset(c3,0,sizeof(c3));
		
	}
	
}

众数

题目描述

众数是指多值集中出现次数最多的数。你每次可以将集合内的某个数加1,请问最多k次操作,你能获得众数最多的出现次数是多少?

比如集合S={1,3,3,4,5}k=2时,我们可以将23分别加1,得到新的集合S={1,4,4,4,5},S的众数为4,出现次数是3

输入格式

第一行是一个整数T (1≤T≤20),表示样例的个数。

每个样例的第一行是两个整数n (1≤n≤105),表示多值集的大小;k (0≤k≤109),表示操作的次数上限。

第二行是n个整数,所有整数处于[1,109]区间内。

输出格式

按顺序每行输出一个样例的结果,为一个整数。

样例输入

3

1 0

1

2 1

1 2

3 1

1 2 2

样例输出

1

2

3

心得与代码

注意题目给的是一个集合,集合中的元素是非降排列的

#include <stdio.h>
//#include <unordered_map>
#include <algorithm>
#include <string.h>
typedef __int64 ll;
using namespace std;

int main(){
	ll N;
	ll num[100005],sum[100005];
//	unordered_map<ll,ll>::iterator iter,it,vit;
	scanf("%I64d",&N);
	while(N--){
//		unordered_map<ll,ll> map;
		ll n,i,k,m,j;
		ll t,cnt,maxnum,maxcnt,sum1;

		scanf("%I64d %I64d",&n,&k);
		for(i=0;i<n;i++){
			scanf("%I64d",&t);
			num[i]=t;
		}
		sort(num,num+n);
	
		sum[0]=num[0];
		    for(i=1;i<n;i++){ //处理前缀和
			sum[i]=sum[i-1]+num[i];
		}
		m=1;
		for(i=0;num[i+m-1]!='\0';){
			maxnum=num[i+m-1];
			if(i==0) sum1=sum[i+m-1];
			else sum1=sum[i+m-1]-sum[i-1];
			
			if(k>=maxnum*m-sum1){
				maxcnt=m;
				m++;
			}
			else{
				i++;
			}
		}
		
		printf("%I64d\n",maxcnt);
		memset(num,0,sizeof(num));
		memset(sum,0,sizeof(sum));
	}

} 

按位与

题目描述

一个正整数的数列A={a1,a2,⋯,an},取区间[l,r],如果我们在al,al+1,⋯,ar中最多去掉k个数,使得这些数按位与的结果是非0,那么我们称这样的区间是合法的。 请求最长的合法区间长度。

按位与,记为&,即将两个数按二进制对位做与运算,比如1(2)=01,2(2)=10, 所以1&2=01(2)&10(2)=00(2)=0

输入格式

第一行是一个整数T(1≤T≤100),表示样例的个数。

每个样例的第一行是两个整数n(1≤n≤10000),k(0≤k≤n)。 第二行是n个正整数ai,1≤ai≤109

输出格式

依次每行输出一个样例的结果,未一个整数。

样例输入

2

4 0

1 2 3 4

4 1

1 2 3 4

样例输出

2

3

心得与代码

思路:将十进制整数转为二进制,将转化后的二进制数保存在数组里,并按位对齐,高位填0,问题转化为求最长的一段区间 ,区间内最多有k个0 。由于转化后的二进制最多32位,所以只要求32次,取32次中最长的一段区间就行

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
typedef __int64 ll;
using namespace std;
char num2[10005][35];
char num3[10005][35];
ll num[10005];
int main(){
	ll N,k,n;
	ll i,j,t;
	scanf("%I64d",&N);
	while(N--){
		scanf("%I64d%I64d",&n,&k);
		memset(num,0,sizeof(num));
		memset(num2,'\0',sizeof(num2));
		for(i=0;i<n;i++){
			scanf("%I64d",&t);
			num[i]=t;
		}
//		for(i=0;i<n;i++){
//		printf("%s\n",num2[i]);
//		}
		for(i=0;i<n;i++){//转二进制
			itoa(num[i],num2[i],2);
//			printf("%s\n",num2[i]);
		}
		memset(num3,'0',sizeof(num3));
		for(j=0;j<n;j++){//按位对齐
			t=0;
			for(i=34;i>=0;i--){
				if(num2[j][i]!='\0'){
					num3[j][t]=num2[j][i];
					t++;
				}
			}
		}
//		for(j=0;j<n;j++){
//		for(i=0;i<34;i++){
//			printf("%c ",num3[j][i]);
//		}
//		 printf("\n");
//		}
		ll l=0,r=0,cnt,ans=0,maxans=0;
		for(i=0;i<=32;i++){
			cnt=0;
			ans=0;
			l=0;
			r=0;
			while(r<n) {
			        if(num3[r][i] == '0') {
			            cnt++;
			        }
			        while(cnt>k) {
			            if(num3[l][i] == '0') {
			                cnt--;
			            }
			            l++;
			        }
			       if(ans<(r-l+1)) ans=r-l+1;
			        r++;
			    }
			if(ans>maxans) maxans=ans;
		}
		printf("%I64d\n",maxans);
	}

} 

图像

题目描述

一个2n×2n的图像,对它的像素点进行扫描,扫描的规则如下:

  1. 如果n=0,那么扫描这个像素点。
  2. 否则,将图片划分成42n−1×2n−1的子图片,按从上到下,从左到右的顺序依次扫描四个子图片。

现在请求第k(从1开始)个扫描的像素点对应的行与列?

输入格式

第一行是一个整数T (1≤T≤10000),表示样例的个数。

每个样例占一行,是一个整数k (1≤k≤260)

输出格式

每个样例的每个查询输出一行,为两个整数,即扫描像素点的行与列(从1开始)。

样例输入

5

2

3

5

7

11

样例输出

1 2

2 1

1 3

2 3

4 1

样例解释

1 2 5 6

3 4 7 8

9 10 13 14

11 12 15 16

心得与代码

将图像分为四个子图,判断k位于哪个子图,将返回坐标定位到子图左上角,并将k减去前面子图的像素数

#include <stdio.h>
//#include <unordered_map>
#include <algorithm>
#include <string.h>
#include <math.h>
typedef __int64 ll;
using namespace std;
ll x,y; //x表示行,y表示列
void TX(ll n,ll k){
	ll i,j,w;
	if(n!=0){
		j=pow(2,2*(n-1));
		w=pow(2,n-1);
//		printf("j=%I64d\n",j);
		for(i=1;i<=4;i++){
			if(i*j>=k) break;
		}
		k-=(i-1)*j;
//		printf("k=%I64d\n",k);
		if(i==1){
			
		}
		else if(i==2){
			y=y+w;
		}
		else if(i==3){
			x=x+w;
		}
		else if(i==4){
			x=x+w;
			y=y+w;
		}
		TX(n-1,k);
	}
}
int main(){
	ll N,k,n;
	ll i,j;
	scanf("%I64d",&N);
	while(N--){
		x=1;
		y=1;
		scanf("%I64d",&k);
		for(i=0;i<=60;i++){
			j=pow(2,2*i);
			if(j>=k) 
			{
				n=i;
				break;
			}
		}
//		printf("n=%I64d\n",n);
		TX(n,k);
		printf("%I64d %I64d\n",x,y);
	}

} 

最大的数

题目描述

一个无前导0的十进制正整数n,如果相邻两个数码不是同奇偶的,那么你可以交换它们。 只要存在这样的情况,你可以无限次数地进行这样的交换。请问你都能得到最大的十进制整数是多少?

比如136014→163014→613014→613104

输入格式

存在多样例,数量不超过100。每行一个样例,为一个正整数n (1≤n≤1010000)

输出格式

每行输出一个样例的结果,为一个整数。

样例输入

136014

1314

1357

样例输出

613104

4131

1357

心得与代码

分奇偶保存数字,然后从开头到结尾进行大小比较

//#include<iostream>
//#include <vector>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
int main(){
//	ios::sync_with_stdio(false);//加快cin,cout的速度

	char num[100005];
	char num1[100005];
	char num2[100005];
	char num3[100005];
 	while(scanf("%s",num)!=EOF){
	 	int i,j,t,flag=0,k,l,b;
//	 	char maxnum,minnum;

		l=num[0]%2;
		j=0;
		k=0;
		for(i=0;num[i]!='\0';i++){ //将数字分奇偶保存
			t=num[i]%2;
			if(t!=l) {
				num1[k]=num[i];
				k++;
			}
			else{
				num2[j]=num[i];
				j++;
			}
		
		}
//		printf("%s\n",num1);
//		printf("%s\n",num2);
		k=0;
		t=0;
			for(j=0,i=0;num2[j]!='\0'&&num1[i]!='\0';){
				if(num1[i]>num2[j]){
					num3[t]=num1[i];
//					k=j+1;
					t++;
					i++;
					flag=1;
//					break;
				}
				else{
					num3[t]=num2[j];
					j++;
					t++;
					flag=1;
		     	}
		}
		if(num2[j]!='\0') {
			for(b=j;num2[b]!='\0';b++){
				num3[t]=num2[b];
				t++;
			}

		}
		if(num1[i]!='\0') {
			for(b=i;num1[b]!='\0';b++){
				num3[t]=num1[b];
				t++;
			}
		}
		printf("%s\n",num3);
        memset(num,0,sizeof(num));
         memset(num1,0,sizeof(num1));
          memset(num2,0,sizeof(num2));
           memset(num3,0,sizeof(num3));
	} 
}

### XTUOJ 平台上的分指针问及解决方法 #### 什么是分指针算法分指针是一种高效的搜索技术,通常用于有序数组中快速定位目标值。该算法通过反复将搜索区间减半来缩小查找范围,从而显著提高效率[^1]。 #### 解决方案概述 对于给定的数据集,在处理过程中遇到不能整除的情况时确实需要注意区分奇数和偶数元素分别存储于不同容器内以便后续计算其均值并继续迭代直至满足终止条件为止。然而这种方法可能会带来较高的时间复杂度因此建议优化策略如下: - **预处理阶段**:读取输入数据的同时完成初步分类工作即将所有数值依据奇偶性质分配至相应列表; - **核心逻辑设计**: - 对每一对相邻项执行平均化运算之前先确认它们是否同属一类(即均为奇数或偶数),只有当两者皆为同一类别成员才允许进行下一步骤; - 计算所得新值应追加到临时缓冲区而非立即覆盖原位置以防破坏原有结构影响后续判断准确性; - **循环控制机制构建**:设置计数器监控剩余待处理单元数量一旦降至单个则停止整个流程输出最终结果。 ```python def process_elements(elements): odd_numbers = [] even_numbers = [] # 将原始序列中的元素按奇偶性分离 for num in elements: if num % 2 == 0: even_numbers.append(num) else: odd_numbers.append(num) while len(odd_numbers) + len(even_numbers) > 1: new_odd = [] new_even = [] # 处理奇数列表 i = 0 while i < len(odd_numbers)-1: avg = (odd_numbers[i] + odd_numbers[i+1]) // 2 new_odd.append(avg) i += 2 # 如果有剩余的最后一个奇数,则保留它 if i < len(odd_numbers): new_odd.append(odd_numbers[-1]) # 类似地处理偶数列表... # 更新旧列表 odd_numbers, even_numbers = new_odd, new_even result = odd_numbers or even_numbers return result[0] if result else None ``` 此段代码展示了如何基于上述原则实现一个简化版的功能模块,实际应用中可能还需要考虑更多边界情况以及性能调优措施以适应更广泛的需求场景。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值