A题 DDL的恐惧
问题描述
ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。
请你帮帮他吧!
input:
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。
每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。
然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。
output:
对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。
思路
这次用一个结构体储存每个作业的分数和ddl,每次取分数最高的一个作业,看ddl之前的日期是否被占用过了,没被占用就用上,否则就舍弃,这里用一个vis数组表示日期是否被占用,由于有多组数据,所以每次都需要重置为0。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct mm
{
int t,v;
bool operator<(const mm&p)
{
return v>p.v;
}
};
bool vis[1001];
mm shu[1002];
int n,T;
int main()
{
scanf("%d",&T);
while(T--)
{
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&shu[i].t);
}
for(int i=0;i<n;i++)
{
scanf("%d",&shu[i].v);
}
sort(shu,shu+n);
int num=0 ;
for(int i=0;i<n;i++)
{
bool flag=1;
for(int j=shu[i].t;j>0;j--)
{
if(vis[j]==0)
{
vis[j]=1;
flag=0;
break;
}
}
if(flag)
num+=shu[i].v;
}
printf("%d\n",num);
}
return 0;
}
总结
这也算是一个典型的贪心问题,
B题 4个数列
问题描述
ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
input:
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
output:
不同组合的数目
思路
这里可以采用二分的思想,一个有四个数组,可以将前后两个分别进行组合相加得到两个数组,数值可能为0的数一定是两个数组中的相反数,只要将其中一个数组排序,用另一个数组的各元素相反数在这一个数组中找到第一次和最后一次出现的位置就能得到出现的次数,总次数就是可能的组合数。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a1[4001],a2[4001],a3[4001],a4[4001];
int b1[16000111],b2[16000111];
int n;
int find(int t,int num)
{
t*=-1;
int first=-1,last=0;
int l=0,r=num-1;
bool f1=0;
while(l<=r)
{
int mid=(l+r)/2;
if(b1[mid]>=t)
{
if(b1[mid]==t)f1=1;
first=mid;
r=mid-1;
}
else l=mid+1;
}
l=0;
r=num-1;
while(l<=r)
{
int mid=(l+r)/2;
if(b1[mid]==t)
{
last=mid;
l=mid+1;
}
else if(b1[mid]>t)r=mid-1;
else l=mid+1;
}
if(f1) {//printf("%d\n",t);
return last-first+1;}
else return 0;
}
int main()
{
int num=0,count=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d%d%d",&a1[i],&a2[i],&a3[i],&a4[i]);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
b1[num]=a1[i]+a2[j];
b2[num++]=a3[i]+a4[j];
}
sort(b1,b1+num);
for(int i=0;i<num;i++)
{
count+=find(b2[i],num);
}
cout<<count<<endl;
return 0;
}
总结
唉,这个万恶的相反数,刚开始看着课上的ppt兴致勃勃的就写,可是每次得到的答案都不对,竟忘了去数组中找的是相反数,
o(╥﹏╥)o
C题 DDL的恐惧
问题描述
TT 是一位重度爱猫人士,每日沉溺于 B 站上的猫咪频道。
有一天,TT 的好友 ZJM 决定交给 TT 一个难题,如果 TT 能够解决这个难题,ZJM 就会买一只可爱猫咪送给 TT。
任务内容是,给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。
TT 非常想得到那只可爱的猫咪,你能帮帮他吗?
input:
多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5
output:
新数组的中位数
思路
首先排序原数组算出新数组元素值的范围,在这个范围中二分答案查找一个P,P是中位数,对于每个P计算P在新数列的名次。
因为原数列已排序,可以去绝对值,得Xj<=Xi+P,枚举下标i然后计算满足条件的下标j的个数,这里的计算也使用二分查找找到第一个大于或等于Xi+P的数的位置,而最终所有满足条件的 j 的总和即符合条件的二元组对数,比较该数和中位数的名次,如果小于中位数的名次则中位数在右侧,如果大于中位数的名次则中位数在左侧,相应更新新数组元素值的范围再进行查找,如果两数相等,则该P就是中位数。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int cat[100001];
int n;
int find(int t )
{
int l=0,r=n-1,ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(cat[mid]>=t)
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%d",&cat[i]);
sort(cat,cat+n);
int l=0,r=cat[n-1]-cat[0],ans=0;
int zh=(n*(n-1)/2+1)/2;
while(l<r)
{
//int mingci=0;
int mid = (l+r)/2;
int mingci = 0;
for(int i=0;i<n;i++)
{
int rr = find(cat[i]+mid);//小于mid的个数
if(rr!=-1)
mingci+= (n-rr);//计算mid名次
}
if(mingci>(n*(n-1)/2-zh))//寻找第一个0出现
{
ans = mid;
l = mid +1;
}
else r = mid;
}
cout<<ans<<endl;
}
return 0;
}
总结
刚开始对PPT中给出的思想第一遍就没听太懂,事后有自己琢磨了老久才弄懂了具体的意思,头冷