程序设计思维与实践 Week4 作业

A - DDL 的恐惧

题意

ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。

解题思路

对于DDL按照分数降序排列,然后遍历DDL,首先尝试将当前DDL安排在ddl当天,如果已经被占用,则向前找时间,直到有空位插入,或者无空位结束。

代码

#include <bits/stdc++.h>
using namespace std;
int ans;
struct DDL
{
	int score;
	int time;
}ddl[1005];

bool cmp(DDL a, DDL b){
	return a.score > b.score;
}
bool timetable[1005];
int main() {
	int T;
	cin >> T;
	while(T--){
		int n;
		cin >> n;
		for(int i = 0; i < n; i++)
			cin >> ddl[i].time;
		for(int i = 0; i < n; i++)
			cin >> ddl[i].score;
		sort(ddl,ddl + n,cmp);
		for(int i = 0; i < n; i++){
			if(timetable[ddl[i].time] == false)
				timetable[ddl[i].time] = true;
			else{
				int j = ddl[i].time - 1;
				while(j > 0){
					if(timetable[j])
						j--;
					else{
						timetable[j] = true;
						break;
					}
				}
				if(j == 0){
					ans += ddl[i].score;
				}
			}
		}
		cout << ans << endl;
		memset(ddl,0,sizeof(ddl));
		memset(timetable,0,sizeof(timetable));
		ans = 0;
	}
	return 0;
}

B - 四个数列

题意

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。

解题思路

  • 首先对数列A和B中任意两项相加的结果进行枚举并存储,最终对其排序
  • 随后枚举数列C和D中任意两项相加的结果,并在枚举时使用二分查找AB数列结果中相反数的个数

代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int ans;
int a[4005],b[4005],c[4005],d[4005];
vector<int> sorted;
int find_first(int a){
	int l = 0,r = sorted.size()-1;
	int ans1 = -1;
	while(l <= r){
		int mid = (l+r)/2;
		if(sorted[mid] >= a){
			ans1 = mid;
			r = mid - 1;
		}else
			l = mid + 1;
	}
	return ans1;
}
int find_last(int a){
	int l = 0,r = sorted.size()-1;
	int ans1 = -1;
	while(l <= r){
		int mid = (l+r)/2;
		if(sorted[mid] == a){
			ans1 = mid;
			l = mid + 1;
		}else if(sorted[mid] < a)
			l = mid + 1;
		else
			r = mid - 1;
	}
	return ans1;
}
int find(int a){
	int x = find_first(a), y = find_last(a);
	if(x != -1 && y != -1 && sorted[x] == a)
		return y - x + 1;
	else return 0;
}	
int main() {
	int n;
	cin >> n;
	for(int i = 0; i < n;i++){
		cin >> a[i] >> b[i] >> c[i] >> d[i];
	}
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			sorted.push_back(a[i]+b[j]);
	sort(sorted.begin(), sorted.end());
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			ans += find(-c[i]-d[j]);
		}
	}
	cout << ans << endl;
	return 0;
}

C - TT 的神秘礼物

题意

给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。

解题思路

  • 本题采用2分答案的做法,首先对cat数组进行升序排列,然后确定ans数组中位数的上下限,对其进行二分,对于任一个答案A,要判断是否为中位数,只需计算A在ans数组中的排名并与中位数的排名作比较即可
  • ans中元素个数为 C n 2 = n ( n 1 ) 2 C_n^2 = \frac{n(n-1)}{2}, 故ans中位数的数组下标(从0开始)为 n ( n 1 ) 2 1 2 \frac{\frac{n(n-1)}{2}-1}{2}
  • 计算某一答案A在ans数组中的下标,即计算 i j , c a t [ j ] c a t [ i ] < x \forall i\neq j,cat[j] - cat[i] < x 的个数,即对 i \forall i 计算 j , c a t [ j ] < c a t [ i ] + x \forall j, cat[j] < cat[i] + x 的个数之和。

代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int cat[100010];
int n;
int find_first(int l, int r, int a)
{
	int ans1 = r + 1;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (cat[mid] >= a)
		{
			ans1 = mid;
			r = mid - 1;
		}
		else
			l = mid + 1;
	}
	return ans1;
}
int ranking(int x)
{
	int ans = 0;
	for (int i = 0; i < n - 1; i++)
	{
		ans += (find_first(i + 1, n - 1,cat[i] + x) - (i + 1));
	}
	return ans;
}
int find()
{
	int l = 0, r = cat[n - 1] - cat[0], mid;
	int s = (n * (n - 1) / 2 - 1) / 2;
	int ans = 0;
	while (l <= r)
	{
		mid = (l + r) / 2;
		int x = ranking(mid);
		if (x > s)
		{
			r = mid - 1;
		}
		else
		{
			l = mid + 1;
			ans = mid;
		}
	}
	return ans;
}
int main()
{
	while (scanf("%d",&n)!=EOF)
	{
		for (int i = 0; i < n; ++i)
		{
			scanf("%d",&cat[i]);
		}
		sort(cat, cat + n);
		cout << find() << endl;
		memset(cat, 0, sizeof(cat));
	}
	return 0;
}
发布了12 篇原创文章 · 获赞 0 · 访问量 254

猜你喜欢

转载自blog.csdn.net/weixin_43851185/article/details/104904627
今日推荐