HDUOJ 1789(贪心)
Doing Homework again
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8869 Accepted Submission(s): 5226
Each test case start with a positive integer N(1<=N<=1000) which indicate the number of homework.. Then 2 lines follow. The first line contains N integers that indicate the deadlines of the subjects, and the next line contains N integers that indicate the reduced scores.
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
0 3 5
这道题的意思大概是:有较多功课要在指定的截止日期前(包括截止日期)完成。每门功课的分数和完成的截止日期不一样,若不能在规定时间完成,则扣除相应功课的分数,并且一天最多只能完成一门功课,问怎么安排才能使减去的分数最小(也可以理解为如何使得到的分数最多)。最后输出扣除的最小分数(不扣除便输出0,意思是扣除0分)
思路:
刚开始看到这道题一点思路也没有,大概想了4个多小时,才找到方法。
首先有两个方向:一是先按截止日期排序,再找相关解决办法。二是按分数大小进行排序,再找进一步的方法。
一:按时间排从小到大排序:
(刚开始认为这种方法得到的分数最大,实际上是错误的)
截止日期 要交功课(多门)的分数 每个截止日期中选择分数最大的一门功课 实际上 得到的分数最大的一组
1 2、3 、4 4 6
2 4、5 5 7
3 6、7、8 8 8
4 5、6 6 6
5 1、1、1 1 1
显然我最开始的想法是不对的,可能别的高手还有别的方法,至少在时间这个方向上我实在想不出办法了(后来听同学说可以用队列来做)。于是我又开始转向到以分数为主线的方向:
二:
(之所以把分数从大到小排序并按此顺序判断并确定相应功课完成的日期,是为了确保分数较大的功课能够被完成,然后再考虑分数小的功课)
(已按从大到小排序) * 已按下面方法操作 * 若不把分数大的功课放在截止日期完成,
截止日期 相应功课的分数 * 完成日期 得到的分数最大的一组 * 完成日期 得到的一组结果(仅以2 7为例验证)
2 7 由 * 2 7 * 1 7
4 7 大 * 4 7 * 4 7
1 6 向 * 1 6 * 3 5
4 5 小 * 3 5 * 5 3
5 3 依 * 5 3 * 2 2
2 2 次 * 7 1 * 7 1
7 1 判断 *
方法:分数从大到小排,若分数一样,按日期从小到大排。
把分数大的功课放在截止日期完成(若把该功课提前(新日期)完成,可能会把原本能在这个新日期完成的分数较大的功课挤下去,以上面的中间和右边的
两组数据为参考:若把2 7变为1 7,就是把这门分数为7而截止日期为2的功课放在第一天完成,则1 6被挤出,2 2,填补原来2 7 的位置 )若该日期已有功课要做,说明这个日期要做的功课的分数比现在这门功课的分数要高,因此现在这门功课要往前推一天,再次判断是否已有功课要做,若没有,则把这门功课放在这天完成,若仍有,则继续往前推。最后若没有找到空天(当前该天还没有功课要做),则不做该门功课,继续下一门功课的判断。(拿上面的左边的数据为例:4 7和4 5,因为4 7在4 5前面,所以当4 5出现时,第4天已有分数为7的功课要做,所以这门分数为5的功课要往前推一天,因为第3天暂时没有功课,所以这门5分的课要放在第3天完成。当2 2出现时,因为第1天和第2天都已有功课要做,所以这门功课不做,继续下一门功课的判断。)
My solution:
/*2015.7.31*/
#include<stdio.h>
#include<stdlib.h>
struct stu
{
int day;
int score;
}per[1100];
int cmp(const void *a,const void *b)
{
if((*(stu *)b).score!=(*(stu *)a).score)
return (*(stu *)b).score-(*(stu *)a).score;
else
return (*(stu *)a).day-(*(stu *)b).day;
}
int c[1100];
int main()
{
int i,j,n,m,sum,b,k;
scanf("%d",&n);
while(n--)
{
scanf("%d",&m);
for(i=0;i<m;i++)
scanf("%d",&per[i].day);
for(i=0;i<m;i++)
scanf("%d",&per[i].score);
qsort(per,m,sizeof(per[0]),cmp);
c[0]=per[0].day;
i=1;/*用来记录已经安排好的功课数目(也可以理解为已安排好日期的天数)*/
sum=0;/*记录扣除的总分数*/
for(j=1;j<m;j++)
{
b=per[j].day;
for(k=0;k<i;k++)
{
if(b==c[k])
{
b--;/*该天已有功课要做,当前功课要往前推1天*/
k=-1;/*往前推1天后,需要重新判断该天是否是空天,因此要从k=0开始判断,*/
} /* 因为循环最后要执行k++,所以这里令k=-1;*/
if(b<=0)/*当前日期及其前面的日期都有安排,该门功课不做*/
{
sum+=per[j].score;
break;
}
}
if(k==i)
c[i++]=b;
}
printf("%d\n",sum);
}
return 0;
}
/*2016.3.27*/
记得之前做过,当再次做时,又忘了,毫无头绪,于是又看了之前的题解,才想起来怎么做。汗汗汗汗!!!!!
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
struct stu
{
int score,time;
}s[1050];
int cmp(stu a,stu b)
{
if(a.score!=b.score)
return a.score>b.score;//分数从大到小排
else
return a.time<b.time;//分数相同则按照截止时间来排,时间越紧急,越靠前排
}
int mark[1010];
int main()
{
int i,j,h,n,k,g,t,sum,fen;
scanf("%d",&g);
while(g--)
{
sum=0;//总分数
fen=0;//能得到的分数
memset(mark,0,sizeof(mark));
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&s[i].time);
}
for(i=1;i<=n;i++)
{
scanf("%d",&s[i].score);
sum+=s[i].score;
}
sort(s+1,s+1+n,cmp);
for(i=1;i<=n;i++)//n门功课
{
t=s[i].time;
if(mark[t]==0)
{
mark[t]=1;//标记该天已用
fen+=s[i].score;
}
else
{
while(mark[t]&&t>0) //往前查找空闲时间来做该道题 ,但t>0
t--;
if(t>0)
{
mark[t]=1;//标记该天已用
fen+=s[i].score;
}
}
}
printf("%d\n",sum-fen);
}
return 0;
}