【游戏通关】解题报告

来源:互联网 发布:淘宝首页装修雪花代码 编辑:程序博客网 时间:2024/04/30 03:28
背景背景   机房里的人都十分认真地在编程,但总有一些人会偷偷玩游戏。。。。。。 问题描述问题描述 问题描述问题描述    XY 经常在机房里偷偷玩游戏,于是他也经常被CJH 教练批评。但屡次的批评一点作用也 没有,你看他又开始玩起了游戏。   这次XY 可碰上难题了,因为据可靠的线报CJH 教练在不久后就回来机房,但XY 需要完 成N 个任务才能将这个游戏通关。   每个任务完成时限T,就是这个任务必须在时间T 之前完成(你可以认为游戏刚开始的时 间为1),还有完成这个任务XY 可以获得一定的奖励W。由于XY 娴熟的技术以及任务的简 单,他可以在一个单位时间将任务完成。   XY 想要在CJH 教练到来之前将任务全部完成,同时他也想获得最多的奖励。这个他本来 可以编程自己完成的,但是为了能马上将游戏通关,他需要全神贯注进去。于是他没有空编 程计算,于是他希望你能帮助他将问题的答案计算出来。 输入格式输入格式 输入格式输入格式   输入数据第一行有一个整数N ,表示需要完成的任务数目;   接下来N  行,每行两个整数T,W  (中间用一个空格隔开),分别表示完成这个任务的最 后期限和完成这个任务后获得的奖励。 输出格式输出格式 输出格式输出格式   输出数据有且仅有一行,只包含一个整数S,表示最多获得的奖励。 样例输入输出样例输入输出 样例输入输出样例输入输出 Sample #1 game.in                                    game.out 2                                          5 1 5 1 4 Sample #2 game.in                                    game.out 5                                          15 2 3 1 2 4 5 1 3 3 4 样例解释样例解释 样例解释样例解释   对于样例2 XY 可以选择完成任务1,3,4 和5,这样他可以获得奖励15。 数据规模数据规模 数据规模数据规模 对于10%的数据,N≤100,Ti≤100,Wi≤2000; 对于30%的数据,N≤1000,Ti≤5000,Wi≤2000; 对于50%的数据,N≤10000,Ti≤20000,Wi≤2000; 对于100%的数据,N≤200000,Ti≤200000,Wi≤2000 。 时间限制时间限制 时间限制时间限制 1s 

这道题一看感觉是一道简单题,就随便想了一个贪心策略来解,结果悲剧地WA0。这是个血的教训啊。听上去讲解的同学,他们的贪心策略都是证明过的。
唉。。。其实考试的时候时间也没有那么紧张,证明一下也何尝不好呢??

我的贪心策略就是,每一时刻选择一个期限为当前时间的最大价值的任务来做。其实这个反例太多,只是我没有去想过。
如四个任务的时间 1 1 2 2,价值1 1 100 100,最大价值为200而不是101。

这道题的好方法有几个。
梁旭罡:
使用了两个接近正解的贪心策略,每次从中取出较小值(这两个贪心策略都是过贪的),结果AC了。这个值得学习。
program seat;const  f1=2;  f2=3;var  ex,jj,jz:array[1..2,1..2]of int64;  n,m:longint;procedure init;begin  assign(input,'seat.in');  reset(input);  assign(output,'seat.out');  rewrite(output);end;function f(n:int64):int64;var  i:longint;  nn:int64;  p:boolean;begin  jz[1,1]:=0;  jz[1,2]:=1;  jz[2,1]:=1;  jz[2,2]:=1;  p:=true;  nn:=n-2;  while nn>0 do begin    if nn and 1=1  then begin      if not p then begin        ex[1,1]:=(jj[1,1]*jz[1,1]+jj[1,2]*jz[2,1])mod m;        ex[1,2]:=(jj[1,1]*jz[1,2]+jj[1,2]*jz[2,2])mod m;        ex[2,1]:=(jj[2,1]*jz[1,1]+jj[2,2]*jz[2,1])mod m;        ex[2,2]:=(jj[2,1]*jz[1,2]+jj[2,2]*jz[2,2])mod m;        jj:=ex;      end      else begin jj:=jz;p:=false end;    end;    ex[1,1]:=(jz[1,1]*jz[1,1]+jz[1,2]*jz[2,1])mod m;    ex[1,2]:=(jz[1,1]*jz[1,2]+jz[1,2]*jz[2,2])mod m;    ex[2,1]:=(jz[2,1]*jz[1,1]+jz[2,2]*jz[2,1])mod m;    ex[2,2]:=(jz[2,1]*jz[1,2]+jz[2,2]*jz[2,2])mod m;    jz:=ex;    nn:=nn shr 1;  end;  case n of    1:begin f:=f1;exit;end;    2:begin f:=f2;;exit;end;  end;  f:=(f1*jj[1,2]+f2*jj[2,2])mod m;end;procedure main;var  t,i:longint;begin  readln(t,m);  for i:=1 to t do begin    readln(n);    writeln(f(n));  end;  close(output);end;begin  init;  main;end.
汪维正+彭靖田
贪心+堆优化
我打出来了这个。
思路:因为正向贪容易推出反例来,因此考虑到了反向来贪心,这是个好思路,值得学习呀。

就是从最大的时间到最小的时间,最后时刻,只能够选择期限为这个时刻的任务,否则就没得做了,因此做这个时刻价值最大的任务,用反证法,如果做其他的任务,解只可能小于或等于最优解。
剩下来的任务保留,或不做,或在前面时刻做,每一个时刻都把期限为当前时刻的任务加入进来,选择最优的,最优的选择同上。
因此考虑使用堆优化。维护一个大根堆。
(常常易忘记的del()函数的返回值)

这个思路不难,也易证明。这个反向来贪心的思路不错!!还有以后时间不紧的情况下一定要证明自己的策略!!
//用堆的贪心方法 #include <cstdio>#include <cstdlib>long n;struct node{long t;long a;};const long oo = 0x7fff0000;long heap[200002];long size = 0;long start[200002];node task[200002];long ans = 0;void swap(long a,long b){long tmp = heap[a];heap[a] = heap[b];heap[b] = tmp;}void adjust_down(long l){while((l<<=1)<size+1){if (l+1<=size&&task[heap[l+1]].a>task[heap[l]].a)l++;if (task[heap[l>>1]].a<task[heap[l]].a) swap(l>>1,l);else break;}}void adjust_up(long l){while (l>1){if (task[heap[l]].a>task[heap[l>>1]].a)swap(l,l>>1);else break;l >>= 1;}}long del(){if (size<=0) return -1;long tmp = heap[1];heap[1] = heap[size];size--;adjust_down(1);return tmp;}void insert(long l){size++;heap[size] = l;adjust_up(size);}int bigger(const void *a,const void* b){long aa = *(long*)a;long bb = *(long*)b;if (aa>bb)return 1;else if (aa==bb)return 0;return -1;}int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);scanf("%ld",&n);for (long i=1;i<n+1;i++){scanf("%ld%ld",&task[i].t,&task[i].a);}qsort(task+1,n,sizeof(node),&bigger);for (long i=n;i>0;i--){if (start[task[i].t]==0)start[task[i].t]=i;}long index = n;for (long i=task[index].t;i>0;i--){while (index>=0&&task[index].t==i){insert(index);index--;}long tmp = del();if (tmp>0)ans += task[tmp].a;}printf("%ld",ans);}

刘德恩:
贪心+并查集
代码比较简短,思路比较巧妙。
仍然是贪心。

每次选择价值最大的任务,如果能做(接下来讨论)就做,不能做则放弃并选择次优。
能做即从1时刻到任务的最后时刻有空可以做,
做一个任务,一定要完成,则尽量晚,更多的任务可以有时间完成。只会更优,不会更差。
这就可以得到一个朴素的贪心策略。

考虑优化,每扫描到一个任务,要让它能够完成,并时间尽量晚,则应该把它插入1~当前时刻离当前时刻最近的点。
因此想到了区间,又因此想到了集合,并查集。
每次插入点的操作都是一次合并的操作。区间中的点的父亲都指向最左边的那一个点的左边,即最早有空隙的地方。
考虑1~i全部合并的情况,即为所有的i父亲指向0。则1~i不再有空隙了。

这是一个好方法,但是不容易想到。

这个给我启示!!
有时候思路要打开。例如这里,要能从插入的空隙联想到连续区间,再联想到并查集,再联想到父亲指向空隙!!
这思维还是比较跳跃的。。。

我的代码
#include <cstdio>#include <cstdlib>struct node{long t;long a;};long fa[200002];int bigger(const void* a,const void* b){node* aa = (node*) a;node* bb = (node*) b;if (aa->a>bb->a)return -1;if (aa->a==bb->a)return 0;return 1;}long getroot(long a){if (fa[a]==a) return a;return fa[a] = getroot(fa[a]);}void merge(long a,long b){fa[getroot(a)] = getroot(b);}long n;node task[200002];long ans = 0;int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);scanf("%ld",&n);long max = 0;for (long i=1;i<n+1;i++){scanf("%ld%ld",&task[i].t,&task[i].a);max >?= task[i].t;}for (long i=0;i<max+1;i++)///////一开始写成从0到n了。这是个时间的区间{fa[i] = i; }qsort(task+1,n,sizeof(node),&bigger);for (long i=1;i<n+1;i++) {long ffa = getroot(task[i].t);if (ffa!=0){ans += task[i].a;merge(ffa,ffa-1);//////我觉得这句话最具有跳跃性。将父亲设为上一个点,//如果是一个空隙,则指向它,如果是一个连续区间,//则意味着于这个区间合并了并指向最左边}}printf("%ld",ans);}


刘德恩:
#include<iostream>#include<cstring>#include<stdio.h>using namespace std;struct ee{       int timee,w;}a[500000];int n,ans;int pre[500000];void init(){     freopen("game.in","r",stdin);     freopen("game.out","w",stdout);}int cmp(const void *a,const void *b){    struct ee *c=(ee *)a;    struct ee *d=(ee *)b;    if(c->w < d->w) return 1;    return -1;}void readdata(){     scanf("%d\n",&n);     int i;     for(i=1; i<=n; i++)     scanf("%d%d\n",&a[i].timee,&a[i].w);     qsort(a+1,n,sizeof(a[1]),cmp);}int getpre(int x){    if(x==pre[x]) return pre[x];    pre[x]=getpre(pre[x]);    return pre[x];}void merge(int x,int y){     int f,ff;     f=getpre(x);     ff=getpre(y);     pre[f]=ff;}void work(){     int i,t,maxx;     maxx=0;     for(i=1; i<=n; i++)     maxx=maxx>a[i].timee ? maxx:a[i].timee;          for(i=0; i<=maxx; i++) pre[i]=i;             for(i=1; i<=n; i++)     {              t=getpre(a[i].timee);              if(t!=0)              {                      ans+=a[i].w;                      merge(t,t-1);              }              if(getpre(maxx)==0) return;     }}void print(){     printf("%d\n",ans);}int main(){    init();    readdata();    work();    print();    return 0;}



原创粉丝点击