week4

A - DDL 的恐惧

 

ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。

所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。

请你帮帮他吧!

 

输入

包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。

每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。

然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。

 

输出

对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。

样例

 

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

12345678910

 

Sample Output

0

3

5

123

 

上方有三组样例。

对于第一组样例,有三个作业它们的DDL均为第三天,ZJM每天做一个正好在DDL前全部做完,所以没有扣分,输出0。

对于第二组样例,有三个作业,它们的DDL分别为第一天,第三天、第一天。ZJM在第一天做了第一个作业,第二天做了第二个作业,共扣了3分,

 

•  优先考虑价值高的任务,把n 个 deadline 根据分数从大到小排序,依次遍历

• 对于第i个 deadline,根据 ti 从后往前遍历,一旦找到空闲时段则安排为第 i 个 deadline。从ddl往前遍历可以保证第 i 个 deadline的安排时间对其他 deadline 的影响最小,因此结果更优

• 时间复杂度较高,为n方,但是简单

直接用一个数组标记

 

 

代码解释:

定义一个结构体ddl,成员有:ddl的时间,任务的分值,jd表示该任务是否完成,初始值设为0,还有任务的编号。重载<号让ddl在堆中按分值大小排列。

在输入ddl信息时,用maxt记下最晚的ddl的时间,在从后往前遍历日期时,从最后一个ddl时间开始–就行了。

从后往前给每天安排任务,每天遍历一遍ddl,找到ddl在当天的加入堆中。这样堆里都是在ddl前可以完成的任务,当堆不为空时,选择堆顶完成,标记任务的 jd = 1 然后pop。

最后统计所有ddl中有哪些任务未完成,累加扣分最后输出。

完整代码

#include <iostream> #include <algorithm> using namespace std; bool cmp(pair<int,int > x, pair<int,int > y) { //first是ddl second是分数 if(x.second == y.second) return x.first > y.first; return x.second < y.second; } int main() { int T,n,sum; cin>>T; while(T--){ cin>>n; sum=0; pair<int,int > a[n]; int mark[10000]={0}; for(int i=0;i<n;i++) cin>>a[i].first; for(int i=0;i<n;i++) cin>>a[i].second; sort(a, a+n, cmp); for(int i=n-1;i>=0;i--) { int ok=0; for(int k=a[i].first;k>0;k-- ) if(mark[k]==0){ mark[k]=1; ok=1; break; } if(ok==0) sum+=a[i].second; } printf("%d\n", sum); } return 0; }

 

 

 

总结

典型贪心问题,只要找到选大尽量放最后的策略即可

 

 

B - 四个数列

 

题目

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。

当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。

请你帮帮他吧!

Input

第一行:n(代表数列中数字的个数) (1≤n≤4000)

接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)

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

1234567

Sample Output

5

1

题解

 

 

在没学之前想暴力搜,四层循环遍历四个数列,直接找出现了和为0的情况,,复杂度n的四次方,不可行。

采用二分法可以降低复杂度

• 枚举A和B,然后枚举C和D,两重循换

• 枚举C和D的时候计算它的相反数在A和B中出现多少次

 

代码:

#include <iostream>
#include <algorithm>
using namespace std;


	int sum1[16000000];
	int sum2[16000000];



int main()
{
	
	int n,mid,l,r,ans = 0; ;
	cin>>n;
	int a[n],b[n],c[n],d[n];

	for(int i=0;i<n;i++)
		cin>>a[i]>>b[i]>>c[i]>>d[i];

		
		
	int count = 0;
	
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			sum1[count]=a[i]+b[j];
			sum2[count]=c[i]+d[j];
			count++;
		}
		
	}
	
	sort(sum2, sum2 + count);

	for(int i=0;i<count;i++)
	{
		l=0;
		r=count-1;
		
		while(l < r)      
		{
			mid =(l+r)/2;
			
			if(sum1[i] + sum2[mid] >= 0)
				r = mid;
			else
				l = mid + 1;        
		}
		while(sum1[i] + sum2[l] == 0 && l < count) 	//判断是否为0及为0的个数 
		{
			ans++;
			l++;            
		} 
	}
	
	
	cout<<ans<<endl;
	return 0; 
 }  
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

 

 

 

 

 

 

总结

用二分法可以大大降低复杂度

在主函数内如果数组过大,可能会runtime error ,可以拿出去

 

 

 

C - TT 的神秘礼物

题目

TT 是一位重度爱猫人士,每日沉溺于 B 站上的猫咪频道。

有一天,TT 的好友 ZJM 决定交给 TT 一个难题,如果 TT 能够解决这个难题,ZJM 就会买一只可爱猫咪送给 TT。

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

TT 非常想得到那只可爱的猫咪,你能帮帮他吗?

Input

多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5

Output

输出新数组 ans 的中位数

样例

Sample Input

4

1 3 2 4

3

1 10 2

1234

Sample Output

1

8

12

题解

 

 

 

如果暴力枚举,复杂度极高,过不了

用二分的思路,可以解决,我们不能直接算出所有的数然后求中位数,但我们知道总数,,我们可以用二分去找,并且比较他之后有多少数,来确定中位数,为了避免绝对值,用最大的数减去最小的数得到的必然为差的最大值,用两次循环和二分,去逼近中值,在循环里,对每一个j,x[i]-x[j]<p,即x[i]-p<x[j],可以找到i的个数,即小于的个数,

 

完整代码

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


int main()
{
	int N; 
	while(scanf("%d",&N)!=EOF)
	{
		int cat[N];
		for(int i=0;i<N;i++)
		{
			scanf("%d",&cat[i]);
		}
		sort(cat,cat+N);
		
	int mid = N*(N-1)/2-(N*(N-1)/2-1)/2;    //中位数位置
	
//第一层二分,二分p值,最后的结果也是二分出来中间 的p值 
		int l=0;
		int r=cat[N-1]-cat[0];
		int m,re;
		while (l<r)           
		{
			m=(l+r)/2;
			int sum=0;		
			for (int j=0; j<N; j++)  //对j进行遍历,找出符合的i 
			{
				int ll=0, rr=N-1, num=0;
				
				
				
				while (ll<=rr)      
				{
					int mm = (ll + rr)/2;
					if (cat[mm] >= cat[j] + m) 
					{
						rr=mm-1;
						num=N-mm;
						

					}
					else ll=mm+1;
				}
				cout<<"!--"<<num<<endl;
				sum+= num;
			}
			
			
			if (sum>=mid)
				l=m+1;
			else r=m;
		}
		l--;
		printf("%d\n", l);
	}
	
}

 

 

总结

此题比较复杂,多次二分可以大大减少复杂度,但是一定得注意二分的条件和sum是大于的个数,因此需要相减

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值