暑期集训第二周与题目精讲(7.17-7.23)

文章介绍了C++中数组的初始化方法,包括一维和二维数组,并讨论了如何将数组元素设为0。同时,提供了多个编程问题的解决方案,涉及最大值计算、字符串比较以及约瑟夫环问题的递归算法实现。此外,文章还探讨了在处理大型数据时,如何确保数据类型正确以避免溢出,并给出了不同情况下的复杂度分析。
摘要由CSDN通过智能技术生成

Point 1

如何将一维数组的所有元素初始化为0?
1.int a[5] = {0};
2.将数组a定义成全局变量或静态变量
3.memset(a,0,sizeof(a))

Point 2

下列描述中,正确的是()
1.int a[3][3] = {1};
定义了数组a,并对数组a赋初值。此时,a[0][0]的值为1,而其
余元素的值都为0。
2.int b[4][3] = {{ }, { }, { },{9}};
定义了数组b,并对数组赋初值。此时,b[3][0]的值为9,而
其余元素的值都为0。
 3.int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
以上2种数组初始化方法是等价的。
int b[4][3] = {{1, 2, 3}, { }, {4, 5}};
4.int b[4][3] = {1, 2, 3, 0, 0, 0, 4, 5};
以上2句是等价的

Point 3

以下字符数组的定义和初始化正确的是
1.char str[ ]="Hello";
2.char str[ ] = {‘H', ‘e', ‘l', ‘l', ‘o', '\0'};
3.char str[ ] = {“Hello"};
4.char str[6] = {‘H', ‘e', ‘l', ‘l', ‘o'};

Point 4

hypot函数头文件math.h或cmath
hypot(a,b)的返回值为double类型

Point 5

Point 6

Point 7

Point 8

Point 9

Point 10

Point 11

Point 12

Point 13

Point 14

Point 15

Point 16

NO.1

题目描述:

题意分析:本题需要通过题目给出的函数返回一个整数,但经过简单的计算可以发现它可能超过整形int的范围,于是返回类型应该设计为long long.

解题思路:题目给出的式子共分两种情况,第一种情况进行递归处理即可。第二种情况是 ∣r-l∣≤5.此时返回的是区间最大值。这里有一个细节是,在max函数里,因为M函数的返回值是long long,而max函数不能把两个不同类型的变量作比较,所以需要把常数7也改为long long类型的,即写作(long long) 7。

复杂度分析:O(n)

带注释的代码:

#include<iostream>
#include<cmath>//这里需要用到max和abs函数,因此要加这个头文件
using namespace std;
int n;
long long a[500005];//注意定义的是长整型
long long M(int l,int r){
	if(abs(r-l)<=5){
		int ans=0;
		for(int i=l;i<=r;i=i+1){
			if(a[i]>ans){
				ans=a[i];
			}//找到a[i]的最大值
		}
		return ans;
	}
	else{
		return (M(l,(l+r)/2)%max(M((l+r)/2+1,r),(long long)7))+a[(l+r)/2]-1;//按照题目要求写,注意数字7要定义为long long
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i=i+1){
		scanf("%lld",&a[i]);
	}
	printf("%lld\n",M(1,n));
	return 0;
}

NO.2

题目描述:

题意分析: 参赛者需要猜测所给的谜底中的所有字母,每猜对一个字母,就算猜中谜底中对应的所有该字母,每猜错一次,刽子手就会在图纸上划一笔,累计画7笔,参赛者就输了,如果在画完之前猜出所有谜底,则参赛者获胜,如果参赛者在猜错7次前放弃,则参赛者退出。

解题思路: 用字符串数组保存三种结果输出;标记谜底中所有字符,统计谜底中字符的种类数,对照参赛者的猜测结果,如果猜到了一种,谜底中剩余未被猜出字符个数减一,猜错了,错误次数加一,全部猜对了,返回0,猜错次数累计7次,返回1,没有输赢,返回2.

复杂度分析:O(n)

带注释的代码:

#include<iostream>
using namespace std;
int check(string &a, string &b) {//a为答案,b为猜侧值
    int t=0;//统计当前还有多少种字符没有被猜出
    int w=0;//记录当前猜错次数
    int num1[26]={0},num2[26]={0};//num[i]统计字符'a'+i是否出现过
    for(int i=0;i<a.length();i=i+1){
	    num1[a[i]-'a']++;
	}//统计字符种类,也可以num1[a[i]-'a']=1,反正就是标记一下
    for(int i=0;i<26;i=i+1){
	    if(num1[i]){
	    	t=t+1;
		}
	}//统计答案中字符种类
    for(int i=0;i<b.length();i++) {//遍历b字符串中各个字符
        if(num2[b[i]-'a']!=1) {//如果b中还没猜过该字符
            if (num1[b[i]-'a']) {//如果a中该字符出现过,猜对一次,剩余未猜中字符数total减一,猜完返回0,str[0]=win
                t=t-1;
                if(t==0) return 0;
            }
            else if(num1[b[i]-'a']!=1) {//当前猜测的字符未在a中出现,说明猜错了,记录猜错次数,达到7次则猜数失败,返回1,str[1]=lose
                w=w+1;
                if(w>=7) return 1;
            }
        }
        num2[b[i]-'a']= 1;//标记当前字符已经猜过了,避免重复统计
    }
    return 2;//猜完未错也未对,出局,str[2]=chicked out
}
int main() 
{
    int n;
    string s1, s2;
    string str[] = { "You win.","You lose.","You chickened out." };//三种情况
    while (~scanf("%d", &n) && n != -1) {
    printf("Round %d\n",n);
    cin>>s1>>s2;
    cout<<str[check(s1,s2)];//通过check()函数返回值作为下标直接输出对应游戏结果
    printf("\n");
    }
    return 0;
}
    

NO.3

题目描述:

#include<iostream>
#include<vector>
using namespace std;
int main() {
    int N,k,m;
    while(~scanf("%d %d %d",&N,&k,&m)){
        if(N==0&&k==0&&m==0){
        	break;
		}
    vector<int>a(N+1);//1~N;
    int i=1,j=N;
    int c1=0,c2=0;//分别记录i查找和j查找的有效次数 
    int ans=0;//记录当前已经挑选了几人 
    int same=0;//记录第一次找到的人的编号,因为两次查找可以找同一个人,但第一次找到的人因为被选中,已经被打上标记了,第二次查找中可能会跳过这个人,然而用same记录,标志这个人可以被选中
    bool flag=0;//标记两次选人是否选择到同一个人身上
    while(ans<N){
    while(1){//约瑟夫环,间隔n(题目中为k),逆时针,也就是顺着编号
    if(!a[i])c1++;
    if(c1%k==0){
    a[i]=1;same=i;c1=0;break;//一次有效间距查找完成后,c1清0; 
    }
    i=i%N+1;//1~N;
    }
    while(1){//顺时针,间隔m,逆着编号
        if(!a[j]||j==same){
		    c2=c2+1;
		}//没有被挑选走或者与当前的i(same)是同一个数,都算一次有效查找 
        if(c2%m==0){
            if(j==same){
            	flag=true;
			}
            else{
        	    a[j]=1;
		    }
            c2=0;//一次有效间距查找完成后,c2清0 
            break;
        }
        j=(j-1+N-1)%N+1;//1~N;
    }
    if(flag){
        printf("%3d",same);
	    ans=ans+1;
        flag=false;//flag状态清零 
    }
    else{
	    printf("%3d%3d",i,j);
		ans=ans+2;
	}
    same=0;//一次进行两种查找,完成后本轮查找的same已经被调用过了 ,需要重新更新以便下一轮查找使用 
    if(ans<N)printf(",");//后续还有人未被挑选,打印后缀','
    else break;//ans>=N,所有人都被挑选完了,任务完成
    while(a[i]){
	    i=i%N+1;
	}//查找下一个未被挑选的i,作为下一次循环查找的出发点 
    while(a[j])j=(j-1+N-1)%N+1;//查找下一个未被挑选的j ,作为下一次查找的出发点
    }
    printf("\n");
    }
    return 0;
}

NO.4

题目描述:

题意分析:有 a 名同学与 n 首歌,给出每位同学从每首歌中获得的快乐值,按照给予的总快乐值即欢乐度从高到低排序,选出前 m 首,并特殊处理zyl最喜欢的歌(如果zyl最喜欢的歌已经在歌单中,把这首歌提到歌单的第一个位置,如果不在,把zyl最喜欢的曲目代替已经选择好的歌单里最后一首歌)求出最终歌单。

解题思路:先不考虑zyl,或者可以采用结构体排序,用一个结构体记录两个信息:欢乐度d和编号id,然后使用 sort,按欢乐度从大到小排序。然后记录她最喜欢歌的编号。在学号为 b 时,使用一个变量maxid记录快乐值最多的歌的编号。然后就是判断了。在排好序后,我们先在前m首歌中找一遍,看看有没有她最喜欢的歌。如果没有,就先输出前m−1首歌的编号,再输出maxid,这就达到了删去最后一首歌,并把她最喜欢的歌放在最后的效果。如果有,那就先输出maxid,然后依次输出前m首歌的编号,但碰到maxid时,就直接跳过。

复杂度分析:O(a+m)

带注释的代码:

#include<bits/stdc++.h>
using namespace std;
struct song{
	int d,id;//d表示欢乐度,id表示编号
}f[100005];
bool cmp(song x,song y){
	return x.d>y.d;//按照欢乐度从大到小排列
}
int maxd=-1,maxid=0;
int n,m,a,b;
int main(){
	scanf("%d %d %d %d",&n,&m,&a,&b);
	for(int i=1;i<=a;i++){
		for(int j=1;j<=n;j++){
			int x;
			scanf("%d",&x);
			if(i==b&&x>maxd){//maxd和maxid存zyl最喜欢的歌的欢乐度和编号
				maxd=x;
				maxid=j;
		    }
			f[j].d+=x;f[j].id=j;
		}
    }
	sort(f+1,f+1+n,cmp);//使用sort函数排序
	int flag=0;
	for(int i=1;i<=m;i=i+1){
		if(f[i].id==maxid){//判断maxid是否在歌单里
			flag=1;
		}
	}
	if(flag==1){
		printf("%d",maxid);//在歌单里,先输出maxid
		for(int i=1;i<=m;i=i+1){
		    if(f[i].id!=maxid){
		       	printf(" %d",f[i].id);//记得跳过maxid,因为之前已经输出了
		    }
		}
	}
	else{
		for(int i=1;i<m;i=i+1){
	        printf("%d ",f[i].id);//不在歌单里,先输出前m-1首
	    }
	    printf("%d",maxid);			
	}	
}

NO.5

题目描述: 

题意分析:输出每次操作后正在访问的网址。

解题思路:运用栈的思维,用两个栈来存,s1存前面的网址,s1的栈顶表示当前页面的网址,s2存后面的网址。

带注释的代码:

#include<iostream>
#include<cstring>
#include<sstream>
#include<stack>
#include<queue>
using namespace std;

int main(){
// BACK:访问上一个界面
// FORWORD:访问下一个界面
// VISIT:访问一个新页面
// QUIT:结束访问
    stack<string> s1, s2;
    s1.push("http://www.acm.org/");
    string s, str;
    //用两个栈来存,s1存前面的,s1的栈顶表示当前页面,s2存后面的
    while(cin >> s && s != "QUIT"){
        if(s == "VISIT"){
            cin >> str;
            s1.push(str);
            //访问新页面,下一页要清空
            while(s2.size()){
                s2.pop();
            }
            cout << str << "\n";
        }else if(s == "BACK"){
            if(s1.size() == 1){
                //如果s1的size为1就不能继续往前了
                cout << "Ignored\n";
                continue;
            }
            s2.push(s1.top());
            s1.pop(); 
            cout << s1.top() << "\n";
        }else if(s == "FORWARD"){
            if(s2.empty()){
                //s2为空就不能下一页了
                cout << "Ignored\n";
                continue;
            }
            cout << s2.top() << "\n";
            s1.push(s2.top());
            s2.pop();
        }
    }
}

NO.6

题目描述:

题意分析:某部队进行新兵队列训练,将新兵按顺依次编号并头开始轮流进行一至二报数、一至三报数直到剩下的人 数不超过三人为止。从头开始一至二报数,应报到二的出列,剩下的按序重新排列

解题思路:采用vector模拟删数的过程

1 2 3 4 5 6 7 8

=>1 3 5 7

复杂度分析:O(n)

带注释的代码:

#include <iostream>
#include <vector>
using namespace std;
int main(){
    int T;
    cin >> T;
    while (T--){
        int n;
        cin >> n;
        vector<int> v;
        for (int i = 1; i <= n; i++)
            v.push_back(i); //输入数据
        while (v.size() > 3){
            int sum = v.size();
            for (int i = 1; i <= sum / 2; i++)//报道2的出列 ,总共要删sum/2个
                v.erase(v.begin() + i);//举例:i=1的时候 ,删去了vector里的2,然后vector里后面的数都往前1格 ,i=2的时候看似删的是3, 其实是4(因为4往前了一格 ,占了3的位置)
                sum = v.size();
                if (sum <= 3)
                break;//模拟了第一轮报1~3的所有报到3的人
                for (int i = 1; i <= sum / 3; i++){
                    v.erase(v.begin() + i * 2);
                }//如果 此时剩的人数比3要多 ,在进行下一轮报数
            }
            for (int i = 0; i < v.size(); i++){
                if (i == 0)
                    cout << v[i];
                else
                    cout << ' ' << v[i];
            }
            cout << '\n ';
       }
    return 0;
}

NO.7

题目描述:

 

题意分析:给定一个数字k,保证前k个人为好人,后k个人为坏人,求解m为多少时约瑟夫环只会把坏人踢出去

解题思路:我们可以发现k很小,最大只有13,因此我们考虑用暴力的方式解决问题。先采用约瑟夫环的递推公式求解每一个k对应的m,然后就用打表得解。

复杂度分析:O(n)

带注释的代码:

#include<iostream>
using namespace std;//运用公式(起+m-1)%(长度)暴力求解每一个m
int a[14]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881};//打表法
int main(){
	int k;
	while(~scanf("%d",&k)){
		if(k==0){
		    break;
		}
		printf("%d\n",a[k]);
	}
}

以下是求解多多项式回归的 MATLAB 代码: ```matlab % 输入数据 x1 = [36.4 37.2 37.2 36.2 31.5 28.9 27.2 26.7 26.1 26.1 25.4 25.3 25.4]'; x2 = [50.0 52.0 49.0 51.0 68.0 74.0 83.0 82.0 88.0 88.0 90.0 88.0 87.0]'; x3 = [982.9 982.2 981.8 981.6 982.3 982.6 983.4 983.5 984.0 983.6 984.4 984.5 984.4]'; y = [-7.30 -7.36 -7.35 -7.33 -7.31 -7.30 -7.26 -7.22 -7.21 -7.23 -7.18 -7.17 -7.14]'; % 构建设计矩阵X X = [ones(size(x1)) x1 x2 x3 x1.^2 x1.*x2 x1.*x3 x2.^2 x2.*x3 x3.^2]; % 求解回归系数 beta = X \ y; % 构建预测模型 model = @(x1,x2,x3) beta(1) + beta(2)*x1 + beta(3)*x2 + beta(4)*x3 ... + beta(5)*x1.^2 + beta(6)*x1.*x2 + beta(7)*x1.*x3 ... + beta(8)*x2.^2 + beta(9)*x2.*x3 + beta(10)*x3.^2; % 预测并绘制拟合图 x1fit = min(x1):0.1:max(x1); x2fit = min(x2):0.1:max(x2); x3fit = min(x3):0.1:max(x3); [X1FIT,X2FIT,X3FIT] = meshgrid(x1fit,x2fit,x3fit); YFIT = model(X1FIT,X2FIT,X3FIT); figure; plot3(x1,x2,x3,'o',x1fit,x2fit,x3fit,'*'); hold on; mesh(X1FIT,X2FIT,X3FIT,YFIT); xlabel('x1'); ylabel('x2'); zlabel('x3'); title('拟合图'); % 绘制残差图 YFIT = model(x1,x2,x3); figure; plot(YFIT - y,'o'); xlabel('样本编号'); ylabel('残差'); title('残差图'); ``` 运行上述代码后,会先绘制拟合图,然后绘制残差图。拟合图中,蓝色的点表示原始数据,红色的点表示拟合,可以看到拟合与原始数据比较接近;残差图中,横轴表示样本编号,纵轴表示残差,残差的分布应该比较均匀,没有明显的规律。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值