nkoj P3320 【小挑战】丢失的珠子

P3320【小挑战】丢失的珠子

时间限制 : 20000 MS 空间限制 : 2500 KB

问题描述
何老板有一盒珠子共 n n n颗,编号 1 1 1 n n n。他一不小心将盒子打翻,所有珠子都散落在地。他一颗一颗地把珠子捡起来,每捡一颗就记录下当前这颗珠子的编号。捡完以后发现少了两颗,请你快速找出少了哪两颗珠子。

输入格式
第一行,一个整数 n n n
接下来一行, n − 2 n-2 n2个空格间隔的整数,表示何老板捡起来的珠子的编号。

输出格式
一行,由小到大排列的两个整数,表示丢失的两颗珠子的编号。

样例输入 1

7
4 1 7 2 5 

样例输出 1

3 6

样例输入 2

10 
4 5 8 3 9 7 1 2 

样例输出 2

6 10

提示
对于30%的数据,有n<=1,000
对于100%的数据,有n<=1,000,000
喜欢用 cin 读入的小盆友,在程序主函数开头加上 ios::sync_with_stdio(false); 可以让程序的读入效率大幅提高

题意概括

找出从 1 1 1 n n n中缺少的两个数

问题分析

看到这个题目,首先想到的就是暴力了
暴力怎么暴?
首先一个循环,读入 n − 2 n-2 n2个数,再来一个循环,检查每颗珠子有没有被捡起

#include<bits/stdc++.h>
using namespace std;
int n,x;
bool flag[1000010];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n-2;i++){
		cin>>x;
		flag[x]=1;
	}
	for(int i=1;i<=n;i++) if(!flag[i]) cout<<i<<" ";
	return 0;
}

算一算时间复杂度: O ( n − 2 ) + O ( n ) O(n-2)+O(n) O(n2)+O(n)约等于 O ( 2 n ) O(2n) O(2n),也就是 O ( 2 , 000 , 000 ) O(2,000,000) O(2,000,000),不会爆。
再来算空间: 1 , 000 , 000 B = 1 , 000 K B 1,000,000B=1,000KB 1,000,000B=1,000KB,题目限制 2500 K B 2500KB 2500KB,(当时小T脑袋一热就交上去了),结果?

事实证明,处理其他操作依然是要占空间的,我们不得不想另一种办法。

如果题目卡着空间不让我们开数组,那我们就考虑……

数学问题!

如果我们建立一个 s u m sum sum变量记录所有珠子的和,每捡起一颗就减去它的编号,剩下的就是丢失的两颗编号之和了。
设丢失的编号为 a , b a,b a,b,即 a + b = s u m − o t h e r ( 被捡起的编号之和 ) a+b=sum-other(被捡起的编号之和) a+b=sumother(被捡起的编号之和)
一个方程,两个未知数,毕达哥拉斯告诉你:这是不可能解出来的。
那我们就还需要一个方程,构成二元方程组。

那么,
剩下的那个方程哪里来呢?
假设一个变量 q d t qdt qdt,记录平方和,同样的,每捡起一颗,就减去它编号的平方和。
则得到: a 2 a^2 a2 + + + b 2 = q d t − o t h e r 2 b^2=qdt-other^2 b2=qdtother2
则得到一个二元方程组,就能解出 a , b a,b a,b了。

示例代码

#include<bits/stdc++.h>
using namespace std;
long long n,x,sum,qdt;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);//快读
	cin>>n;
	sum=n*(n+1)/2,qdt=n*(n+1)*(2*n+1)/6;
	for(int i=1; i<=n-2; i++) {
		cin>>x;
		sum-=x;
		qdt-=x*x;
	}
	for(int i=1; i<=sum; i++)
		if(i*i+(sum-i)*(sum-i)==qdt) {//解方程,枚举
			cout<<i<<" "<<sum-i;
			return 0;
		}
	return 0;
}

没有数组,just a Maths Question!

接下来为拓展延伸板块

接着我为了检查对错,写了个对拍。
即自己造样例自己测,生成样例时要与格式保持一致。
这里需要用到rand()函数,包含在<stdlib.h>头文件里,
生成随机时间种子需要用到time()函数,不同的时间生成会产生不同的数,包含在<time.h>头文件里

对拍需要一个正解代码,一个暴力代码,一个生成样例的程序

示例

//ZhengJie.cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,x,sum,poq;
signed main() {
	freopen("Data.in","r",stdin);
	freopen("ZhengJie.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	sum=n*(n+1)/2,poq=n*(n+1)*(2*n+1)/6;
	for(int i=1; i<=n-2; i++) {
		cin>>x;
		sum-=x;
		poq-=x*x;
	}
	for(int i=1; i<=sum; i++) if(i*i+(sum-i)*(sum-i)==poq) {
			cout<<i<<" "<<sum-i;
			return 0;
		}
	return 0;
}

//Baoli.cpp
#include<bits/stdc++.h>
using namespace std;
int n,x;
bool flag[1000010];
int main(){
	freopen("Data.in","r",stdin);
	freopen("Baoli.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n-2;i++){
		cin>>x;
		flag[x]=1;
	}
	for(int i=1;i<=n;i++) if(!flag[i]) cout<<i<<" ";
	return 0;
}

//MakeData.cpp
#include<bits/stdc++.h>
using namespace std;
bool flag[1000010];
int main() {
	freopen("Data.in","w",stdout);
	srand(time(NULL));
	int n=rand()%1000000+1;
	printf("%d\n",n);
	for(int i=1; i<=n-2; i++) {
		int x=rand()%n+1;
		while(1){
			if(!flag[x]) break;
			x=rand()%n+1;
		}
		flag[x]=1;
		printf("%d ",x);
	}
	return 0;
}

//Data.in(展示1组) 
74
12 40 31 8 69 26 51 2 45 57 20 60 17 29 14 64 22 7 47 35 70 62 65 1 10 63 41 37 52 3 55 30 11 28 23 5 15 58 49 43 34 36 59 50 6 74 32 54 4 27 66 18 25 53 44 46 38 73 13 61 9 56 39 68 42 48 67 72 71 21 19 24

生成样例后,要用freopen()读入数据,在保存在一个.out文件中,最后写一个.bat程序。

程序里应该写什么?

@echo off
:loop
MakeData.exe
ZhengJie.exe
Baoli.exe
fc ZhengJie.out Baoli.out
if not errorlevel 1 goto loop
pause
:end

最后关闭,双击打开后就可以进行比较了。
你可以看到你暴力程序和正解程序的输出一不一致,从而帮助你调试代码。
这种方法被我们成为对拍。

感谢观看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值