JXNU20级第六次周练题解

niconiconi

JXNU20级第六次周练题解_第1张图片

三角形面积 S = ( A B ∗ s + B C ∗ t + A C ∗ u ) / 2 = A B ∗ h / 2 S = (AB * s + BC * t + AC * u)/2 = AB * h / 2 S=(ABs+BCt+ACu)/2=ABh/2 (h为三角形在AB边上的高)

因为这是等边三角形,所以 s + t + u = h = 3 ∗ A B / 2 s+t+u = h = \sqrt3 * AB / 2 s+t+u=h=3 AB/2

#include
#include
 
double xa,ya,xb,yb,xc,yc,xp,yp;
 
int main(){
    while(~scanf("%lf%lf",&xa,&ya)){
        scanf("%lf%lf",&xb,&yb);
        scanf("%lf%lf",&xc,&yc);
        scanf("%lf%lf",&xp,&yp);
        double s=sqrt((xa-xb)*(xa-xb)+(ya-yb)*(ya-yb));
        double h= 0.5*sqrt(3)*s;
        printf("%.6lf\n",h);
    }
    return 0;
}

奶茶加珍珠

因为奶茶最便宜的都要2元,而珍珠的价格也是2元,而加第一份珍珠相当于2元买了同样的一杯奶茶,而第二份珍珠相当与2元买了2杯奶茶,所以肯定不会买两杯奶茶,只需要买一杯奶茶,剩下的钱全部加珍珠就能得到最大的满足感

#include

int price,love;

int main(){
    int n;
    while(~scanf("%d",&n){
    	int ans = 0;//最大的满足感
    	for(int i = 0; i < n; i++){
            scanf("%d",&price);//价格
        	love = price;//满足感
          	while(price < 19){
              love *= 2;price += 2;
            }
        ans = max(ans,love);
        }
    	printf("%d\n",ans);
    }
}

朝阳摆花

要想丑陋度最小,按照顺时针摆花 花的高度应该先上升后下降;当然先下降后上升也行

用数组表示既为数组是先上升后下降的,数组最后一个元素和第一个元素相邻

先假设花的高度为 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] [1,2,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]

先摆放高度为1的花 序列为 [ 1 , n u l l , n u l l , n u l l , n u l l , n u l l , n u l l , n u l l ] [1,null,null,null,null,null,null,null] [1,null,null,null,null,null,null,null] null表示该位置没有摆放花

然后把2和3摆放到nul的最左端和最右端,得到 [ 1 , 2 , n u l l , n u l l , n u l l , n u l l , n u l l , 3 ] [1,2,null,null,null,null,null, 3] [1,2,null,null,null,null,null,3]

接下来依次摆放高度为4和5的花 [ 1 , 2 , 4 , n u l l , n u l l , n u l l , 5 , 3 ] [1,2,4,null,null,null,5, 3] [1,2,4,null,null,null,5,3]

然后再摆放高度为6和7的花 [ 1 , 2 , 4 , 6 , n u l l , 7 , 5 , 3 ] [1,2,4,6,null,7,5, 3] [1,2,4,6,null,7,5,3]

最后摆放高度为8的花 [ 1 , 2 , 4 , 6 , 8 , 7 , 5 , 3 ] [1,2,4,6,8,7,5, 3] [1,2,4,6,8,7,5,3]

因为某朵花 a i a_i ai 要与两朵花 a i + 2 a_{i+2} ai+2 a i − 2 a_{i-2} ai2 相邻,所以可以对排序后的花隔一个遍历,计算它们高度差的最大值。

注意当 n = 2 n = 2 n=2时,只有两朵花,直接计算它们的高度差。

#include
//这个是万能头文件,加入这一个就加了绝大部分算法竞赛需要的同文件
//例如stdio.h,iostream,algorithm,vector等等
//如果CB或者DEV C++没有这个头文件需要手动去网上找方式添加,
//在HDU和其他大部分OJ平台cpp提交都是支持这个头文件的

using namespace std;
typedef long long ll;

ll h[5005];
 
int main(){
    int n;
    while(~scanf("%d",&n)){
        for(int i = 0 ; i < n ; ++i) scanf("%lld",&h[i]);
        sort( h ,h + n);//这里是对数组h内元素排序,如果不懂的可以写个冒泡排序
        ll i = 0,ans = h[1] - h[0];
        while(i+2 < n){
            ans = max(ans,h[i+2] - h[i]);
            i++;
        }
        printf("%lld\n",ans);
    }
}

朝阳爱奇数

把所有偶数除2一直除到奇数,统计次数即可

#include
//下面涉及到的位运算符号有兴趣的同学可以百度了解
int main(){
    int n;
    while(~scanf("%d",&n)){
        long long a,ans = 0;
        for(int i = 0 ; i < n ; ++i){
            scanf("%lld",&a);
            while(!(a & 1)){// &为位运算符 (a & 1) == (a % 2)
                // !为取反 !false == true  !true == false
                ++ans; a >>= 1;// >>为左移运算符,相当于 /2 即 (a>>1)==(a/2)
            }
        }
        printf("%lld\n",ans);
    }
}

拯救雪乃酱

一个文本的输入,一个字母的输出,可以一直处理到文件结尾。接下来统计计数即可。

#include
#define N 30
int a[N];

int main() {
	char ch;
	while (~scanf("%c", &ch)) {
		if (ch >= 'a' and ch <= 'z')
			++a[ch - 'a'];
	}
	int pos = -1, maxi = -1;
	for (int i = 0; i < 30; ++i)
		if (maxi < a[i]) {
			maxi = a[i];
			pos = i;
		}
	printf("%c\n", 'a' + pos);
	return 0;
}

财政报告

注意观察输入范围 [ 1 , 2 64 ) [1,2^{64}) [1,264),出这个题目的目的就是让做题人真实掌握 c / c p p c/cpp c/cpp中数据类型的数据范围。一个字节存在八个二进制位。但是注意可能存在一个符号位,带 u n s i g n e d unsigned unsigned前缀的数据类型才不存在符号位。

  • char 1字节 [ − 2 7 , 2 7 − 1 ] [-2^7,2^7-1] [27,271]
  • bool 1字节 [ 0 , 1 ] [0,1] [0,1]
  • int 4字节 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [231,2311]
  • unsigned int 4字节 [ 0 , 2 32 − 1 ] [0,2^{32}-1] [0,2321]
  • double 8字节
  • long long 8字节 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [263,2631]
  • unsigned long long 8字节 [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,2641]

算法竞赛中基本需要的都被列举在上方,有几个建议,有关浮点数统一使用 double,当 double 精度都不够的时候使用 long double,整数输入以及计算的时候,自己时刻需要清醒的判断是否会溢出int,如果溢出需要转成 long long 去计算,涉及 1 e 9 + 7 1e9+7 1e9+7取模的时候也是需要先开 long long 计算。


回到这个题目,看到数据范围,使用 unsigned long long 就可以保存下去,不需要开大数去模拟。
使用变量保存之后,因为涉及 a − b a-b ab操作,在无符号变量做减法时,需要小心,因为它不能计算到正确的负数答案。所以我们只需要判断一下 a , b a,b a,b大小是否需要输出负号,这个题目就写完了。

#include 
using namespace std;
typedef unsigned long long ull;

ull a, b;

int main() {
	cin >> a >> b; // scanf("%llu %llu",&a,&b);
	if (b > a) { swap(a, b); cout << '-'; }
	cout << a - b << endl;
	return 0;
}

队员的能力值

本题目的就是为了让你们学会计数排序。本题时间被我精心设计了,只能在时间复杂度 O ( n ) O(n) O(n)的情况下才能不超时。也就是卡掉了使用快速排序 s o r t ( ) sort() sort()方法。
本题的 n ≤ 1300000 n\leq1300000 n1300000,但是 a i ≤ 50 a_i\leq50 ai50,我们使用一个 c n t cnt cnt数组统计其中每个数字出现过几次。
接下来是不是只需要把 c n t cnt cnt数组从 1 1 1 50 50 50全部遍历一遍就可以输出从小到大的序列了。

#include 
#define N 51
int a[N];

int main() {
	int n;
	scanf("%d", &n);
	int i, j;
	for (i = 1; i <= n; ++i) {
		int x;
		scanf("%d", &x);
		++a[x];
	}
	for (i = 1; i < N; ++i) {
		for (j = 1; j <= a[i]; ++j)
			printf("%d ", i);
	}
	puts("");
	return 0;
}

解方程

首先不考虑任何优化的前提下使用三重循环依次判断时间复杂度 O ( n 3 ) O(n^3) O(n3)
这个在 n ≤ 1000 n\leq1000 n1000时,最坏情况下需要枚举的循环次数达到了 1 e 9 1e9 1e9的规模。这在 1 1 1s的时间是不可能完成的。特别还有多组输入的情况。
1 1 1s的时间限制,极限的循环次数应该也就是 1 e 8 1e8 1e8了。但是也尽量不要达到这个规模,考虑优化自己的算法,因为当你的算法常数比较大的时候,也是会超时的。


回来这个题目尽量三重循环不行,我们能不能优化掉一层循环,我们最后还需要枚举最后一个数嘛?是不是固定前面两个最后一个数也就与之固定了呢?答案是肯定的。
那么我们就可以使用二分查找去数组中找那个符合的答案。但是二分查找前提是数组有序。排序的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),在使用快速排序的前提下,后面两层循环+二分查找的时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
很显然整个算法的时间复杂度就是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)了,所以说使用冒泡排序也可以通过。

方案一:标准的c语言版

#include 
#include 
#define N 1007
int a[N];

void BubbleSort(int n) {
	bool flag = 0;
	while (!flag) {
		flag = 1;
		for (int i = 1; i <= n - 1; ++i)
			if (a[i] > a[i + 1]) {
				int tmp = a[i + 1];
				a[i + 1] = a[i];
				a[i] = tmp;
				flag = 0;
			}
		--n;
	}
}

bool check(int n, int x) {
	int l = 1, r = n;
	while (l <= r) {
		int mid = (l + r) >> 1; //保证了不溢出int
		if (a[mid] == x)	return true;
		else if (a[mid] > x)	r = mid - 1;
		else l = mid + 1;
	}
	return false;
}

int main() {
	int n, x;
	while (~scanf("%d %d", &n, &x)) {
		for (int i = 1; i <= n; ++i)	scanf("%d", a + i);
		BubbleSort(n); //冒泡排序为后序二分答案做准备
		int flag = 0;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				int c = a[i] * x * x + a[j] * x;
				if (check(n, -c)) { //去a[]中二分查找是否存在a[pos] + c = 0
					puts("YES");
					flag = 1;
					break;
				}
			}
			if (flag)	break;
		}
		if (!flag)	puts("NO");
	}
	return 0;
}

方案一:cpp进阶版

#include 
using namespace std;
#define N 1005

int main() {
	int a[N];
	int n, x;
	while (cin >> n >> x) {
		for (int i = 0; i < n; i++)
			cin >> a[i];
		bool flag = false;
		sort(a, a + n); // algorithm库中的cpp快排函数,十分好用建议百度学习用法
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				int c = -(a[i] * x * x + a[j] * x);
				if (binary_search(a, a + n, c)) { // algorithm库中的二分查找函数,查找区间中是否存在c,返回的bool值
					flag = true;
					break;
				}
			}
			if (flag)
				break;
		}
		if (flag)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
	}
	return 0;
}

还有一种。使用一个很大的桶,保证可以记录出现过的 c c c。这样我们就省略掉二分查找这个步骤,直接看桶中是否存在这个解。还有一个要注意的点就是,数组下标不能出现负数,所以我们需要使用整体后推的办法。也就是把 − 1000 -1000 1000看作 0 0 0,把 0 0 0看作 1000 1000 1000,把 1000 1000 1000看作 2000 2000 2000去判断。这样就可以直接通过下标索引判断真假而找到答案。
这种做法时间复杂度 O ( n 2 ) O(n^2) O(n2),但是需要多开空间去标记。牺牲空间换时间。当 n ≤ 5000 n\leq5000 n5000时使用上方的做法就可能会超时了,需要掌握这种桶标记的方案。

方案二:桶标记法

#include
using namespace std;
const int maxn = 1e3 + 5;

int c[2500], a[1005];
int main() {
	int n, x;
	while (cin >> n >> x) {
		bool flag = false;
		memset(c, 0, sizeof(c));
		for (int i = 0; i < n; ++i) {
			cin >> a[i];
			c[a[i] + 1000] = 1;
		}
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				int tmp = 1000 - a[i] * x * x - a[j] * x;
				if (tmp >= 0 && tmp <= 2000 && c[tmp]) {
					flag = true;
					puts("YES"); break;
				}
				if (flag) break;
			}
		}
		if (!flag) puts("NO");
	}
	return 0;
}

快发奖金!

首先先看每个人需要的金钱是不会改变的,那么这个题目题意翻译就是每一个点权值是恒定不变的,你需要查找区间 [ l , r ] [l,r] [l,r]的和。
接下来就要解释一个算法知识点了,叫做前缀和
如果我需要查询 T T T次,每次都查询 [ 1 , n ] [1,n] [1,n]的和,如果每次你都从头算,那么需要算 T ∗ n T*n Tn次,而且非常非常多的重复计算,当这个 T , n T,n T,n比较大的时候就会超时。
使用一个数组 s u m sum sum,如果原来数组是 a a a。那么我们规定, s u m i = ∑ j = 1 j = i a j sum_i = \sum_{j=1}^{j=i}a_j sumi=j=1j=iaj
那么如果我们需要查找区间 [ l , r ] [l,r] [l,r]的和,那么对应的就是 s u m r − s u m l − 1 sum_r-sum_{l-1} sumrsuml1
再来看这个题目,也就是前缀和的应用了,虽然我考虑到主要是打算介绍,不使用前缀和也是可以通过的。

#include 
typedef long long ll;

ll rnk[5] = { 0,5,17,43,149 };

ll a[50004];
ll sum[50004];

ll mypow(ll x, int y) { //整数幂避免使用math.h中的pow函数防止精度问题
	ll res = 1;
	while (y) {
		res *= x;
		--y;
	}
	return res;
}

int main() {
	/*freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);*/
	int n, ca = 0;
	sum[0] = 0;
	while (~scanf("%d", &n)) {
		int id, mons;
		for (int i = 1; i <= n; i++) {
			scanf("%d %d", &id, &mons);
			a[i] = rnk[id] * mypow(2, mons / 12);
			// a[i] = rnk[id] << (t / 12) 左移运算符计算结果是相同的
			sum[i] = sum[i - 1] + a[i]; //前缀和,可以直接找到[l,r]区间的和
		}
		printf("#Table %d\n%lldG\n", ++ca, sum[n]);
		int m, l, r;
		scanf("%d", &m);
		while (m--) {
			scanf("%d%d", &l, &r);
			printf("%lldG\n", sum[r] - sum[l - 1]);
		}
	}
	return 0;
}

你可能感兴趣的:(JXNU20级第六次周练题解)