A DDL 的恐惧
题目
input&&output:
Sample:
#input:
3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4
#output:
0
3
5
题解
1. 本题我们可以发现对于每个DDL影响它的是它的截至日期与对应分数
2. 首先我们思考如果所有DDL截至日期相同,那么我们肯定优先选择对应分数高的做
3. 然后我们加上截至日期不同这一条件,我们发现每件事在它截至日期当天做对其
它的ddl没有影响,然鹅它之后的ddl是否存在会对他造成影响,如果他之后存在未做
的ddl且相应分数比他高那么我们应该优先做它之后的而他则被放到他之前的日期里,
为了实现这一设想我们发现必须从最后一天开始安排,这样可以保证前面的事件如果
被后面的ddl改变是因为当前事件之后的日期均被占满,且后面的ddl收益更高。
4.我们可以发现如果从最后一天开始取,每天只取一件ddl,而剩余事件在剩余天内
尽量被完成,因此我们发现这是一个大根堆,故我们可以通过构造大根堆来实现
5.最终的损失就是大根堆中剩余ddl的损失之和
C++代码
#include<iostream>
#include<queue>
using namespace std;
priority_queue<int> que;
const int MAXN = 1000;
int MAX = 0;
int ddl[MAXN],score[MAXN];
void init(){
for(int i = 0;i<MAXN;i++){
ddl[i] = 0;
score[i] = 0;
}
}
void Read(int n){
int curr;
for(int i = 0;i<n;i++){
cin>>curr;
ddl[i] = curr;
if(curr>MAX) MAX = curr;
}
for(int i = 0;i<n;i++){
cin>>curr;
score[i] = curr;
}
}
void sort(int n){//按dll由大到小排序
int mid;
for(int i = 0;i<n-1;i++){
for(int j = 0;j<n-i-1;j++){
if(ddl[j]<ddl[j+1]){
mid = ddl[j];
ddl[j] = ddl[j+1];
ddl[j+1] = mid;
mid = score[j];
score[j] = score[j+1];
score[j+1] = mid;
}
}
}
}
int dd(int n){
int sum = 0;
int j = 0;
for(int i = MAX;i>0;i--){
while(j!=n&&ddl[j] == MAX&&MAX==i){que.push(score[j]);j++;}
if(!que.empty()) que.pop();
if(j != n)MAX = ddl[j];
}
while(!que.empty()){
sum+=que.top();
que.pop();
}
return sum;
}
int main(){
int n,num;
cin>>n;
for(int i = 0;i<n;i++){
cin>>num;
Read(num);
sort(num);
cout<<dd(num)<<endl;
}
return 0;
}
B 四个数列
题目
input&&output:
Sample:
#input:
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45
#output:
5
题解
1.首先我们看一下数据量为1e3 四个数组求和如果暴力做复杂度为 n^4 时间大概为1e12
显然会超时
2.我们需要优化它,我们可以发现四组数之和为0 也就是任意两组数之和为另外两组
数之和的相反数,假设我们可以记录前两组数所有和的出现次数,然后从后两组数和
中寻找相反数,然后将次数加一,如果我们可以令这个寻找和记录的复杂度均在log^n
那么就不会超时
3.如果采用stl的map虽然后面查询的复杂度可以达到,但是插入的复杂度为nlogn,因
此不可行(hash_map貌似可以 ——猜测23333)
4.因此我们采用一种比较简单的方式存储结果,直接加起来存在另一个数组中
5.接下来我们需要在结果数组中寻找后两个数之和的相反数,暴力显然会出错,因此
我们想到利用二分(然鹅二分需要先排序 不过sort已经替我们解决了),我们要做的
只是利用二分找到相反数的范围然后求出区间长度就可以了
C++代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> a(0),b(0),c(0),d(0),e(0),f(0);
void Read(int n){
int c1,c2,c3,c4;
for(int i = 0;i<n;i++){
cin>>c1>>c2>>c3>>c4;
a.push_back(c1);
b.push_back(c2);
c.push_back(c3);
d.push_back(c4);
}
}
int find_fir(int ser){
int min = 0,max = e.size()-1,pos = -1,mid = (min+max)/2;
while(min<=max){
if(e[mid]>=ser){
if(e[mid] == ser) pos = mid;
max = mid-1;
}else{
min = mid+1;
}
mid = (min+max)/2;
}
return pos;
}
int find_las(int ser){
int min = 0,max = e.size()-1,pos = -1,mid = (min+max)/2;
while(min<=max){
if(e[mid]>ser){
max = mid-1;
}else{
if(e[mid] == ser) pos = mid;
min = mid+1;
}
mid = (min+max)/2;
}
return pos;}
int times(int n){
int cou = 0;
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
e.push_back(a[i]+b[j]);
}
}
sort(e.begin(),e.end());
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
if(find_fir(-(c[i]+d[j]))!=-1){
cou+= find_las(-(c[i]+d[j]))-find_fir(-(c[i]+d[j]))+1;
}
}
}
return cou;
}
int main(){
int n;
cin>>n;
Read(n);
cout<<times(n)<<endl;
return 0;
}
C
题目
input&&output:
Sample:
#input:
4
1 3 2 4
3
1 10 2
#output:
1
8
题解
1.首先考虑暴力,复杂度n^2,显然超时
2.本题我们发现我们虽然不知道答案的大小但知道答案的位置,对于中位数而言,在
数组中右(len+1)/2个数比他小,因此我们只要找到这么一个数,在数组中比他大
的数恰好有(len+1)/2个,一种方法是遍历可能的答案但耗时较高,我们发现我们已
经知道了答案的范围因此我们可以采取二分的方法去搜寻答案,如果当前答案,偏
小我们就想更大的方向二分,反之亦然
3.接下来我们要处理的就是如何计算数组中插值比答案小的个数呢,我们可以发现
倘若数组有序,那么对于每个元素来说,它与其他元素(比它小)的差值小于答案
将是连续区间内的数,因此我们首先将数组排序,然后对每个数去寻找它可以到达
的最远位置(插值小于答案的最小位置)然后我们将这段区间长度加入到当前答案
的“排名“中,最终即可找到正确答案
4.上述方法复杂度为 n(logn)^2因此需要使用 scanf或者关掉cin同步
std::ios::sync_with_stdio(false);
C++代码
/*
二分答案 二分查询 n(logn)^2;
*/
#include<iostream>
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> v(0);
int find(int j,int x){
int min = 0,max = j,mid,pos;
while(min<=max){
mid = (min+max)/2;
if(v[j] - v[mid] <= x){
pos = mid;
max = mid-1;
}else{
min = mid+1;
}
}
return pos;
}
int main(){
int n,c,ans;
while(scanf("%d",&n)!=EOF){
for(int i = 0;i<n;i++){
scanf("%d",&c);
v.push_back(c);
}
sort(v.begin(),v.end());
int amin = 1,amax = v[v.size()-1],amid;
int min,max,pos = 0,sum = 0,p = ((n-1)*n/2+1)/2;
while(amin<=amax){
pos = 0;
amid = (amin+amax)/2;
for(int i = 1;i<n;i++){
pos = find(i,amid);
//cout<<"pos:"<<pos<<endl;
sum+=i-pos;
}
if(sum >= p){
amax = amid-1;
ans = amid;
}else{
amin = amid+1;
}
//cout<<amid<<" "<<sum<<endl;
sum = 0;
}
cout<<ans<<endl;
v.clear();
}
return 0;
}