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}, Cn2=2n(n−1),故ans中位数的数组下标(从0开始)为 n ( n − 1 ) 2 − 1 2 \frac{\frac{n(n-1)}{2}-1}{2} 22n(n−1)−1
- 计算某一答案A在ans数组中的下标,即计算 ∀ i ≠ j , c a t [ j ] − c a t [ i ] < x \forall i\neq j,cat[j] - cat[i] < x ∀i=j,cat[j]−cat[i]<x的个数,即对 ∀ i \forall i ∀i计算 ∀ j , c a t [ j ] < c a t [ i ] + x \forall j, cat[j] < cat[i] + x ∀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;
}