zoukankan      html  css  js  c++  java
  • P1280 尼克的任务

    P1280 尼克的任务

    题目描述

    尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。

    尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完戍,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。

    写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。

    输入输出格式

    输入格式:

    输入数据第一行含两个用空格隔开的整数N和K(1≤N≤10000,1≤K≤10000),N表示尼克的工作时间,单位为分钟,K表示任务总数。

    接下来共有K行,每一行有两个用空格隔开的整数P和T,表示该任务从第P分钟开始,持续时间为T分钟,其中1≤P≤N,1≤P+T-1≤N。

    输出格式:

    输出文件仅一行,包含一个整数,表示尼克可能获得的最大空暇时间。

    输入输出样例

    输入样例#1:
    15 6
    1 2
    1 6
    4 11
    8 5
    8 1
    11 5
    
    输出样例#1:
    4

    提示:

    由后面状态更新前面状态的DP。

    (填表法和刷表法)

    设计状态f【i】表示在i~T(T为总时间)的时间内的最大闲暇时间

    状态转移方程:

    如果时间点i处没有任务,则f【i】= f【i+1】+1;

    f【i】= f【i+1】(前面的空闲时间)+1(现在的空闲时间);

    如果时间点i处有任务,则f【i】=max(f【i+任务k的持续时间】); //此处k = 1~时间点i处的任务总个数

    所以很容易发现,想要知道f【i】,必须先求出 f【比i大的时间点】,所以确定本题倒推:由f【T】递推到f【1】。

    那么如何记录下 时间点i处的每个任务的信息呢,直接将“任务(开始时间、持续时间)”定义成结构体。输入完毕后,以每个任务的开始时间为关键字 从大到小排序。这样就使得 开始时间相同的任务 靠到了一起,并且接下来dp时直接从下标1开始做,就保证了倒推(时间从后往前,因为排过序)。

     
     
     1 #include <bits/stdc++.h>
     2 const int INFINITE=0x3fffffff;
     3 const int MAXN=1e4+10;
     4 using namespace std;
     5 struct taskNode{
     6     int s;//start
     7     int l;//last
     8     taskNode(int s,int l){
     9         this->s=s;
    10         this->l=l;
    11     } 
    12     taskNode(){}
    13     bool operator <(const taskNode &p1) const{
    14         if(this->s==p1.s) return this->l>p1.l;
    15         return this->s>p1.s;
    16     }
    17 }task[MAXN];//写了有参构造函数之后,空构造函数没有了 
    18 int f[MAXN],sch[MAXN];
    19 int t,n;
    20 
    21 void print();
    22 void init(){
    23     cin>>t>>n;
    24     for(int i=1;i<=n;i++){
    25         cin>>task[i].s>>task[i].l;
    26         sch[task[i].s]++;
    27     } 
    28     sort(task+1,task+n+1);
    29 }
    30 
    31 void dp(){
    32     memset(f,0,sizeof(f));
    33     for(int i=t,now=1;i>=1;i--){
    34         if(sch[i]==0) f[i]=f[i+1]+1;
    35         else for(int j=1;j<=sch[i];j++,now++){
    36             f[i]=max(f[i],f[i+task[now].l]);
    37         }
    38     }
    39 }
    40 
    41 int main(){
    42     //freopen("in.txt","r",stdin);
    43     init();
    44     
    45     dp();
    46     //print();
    47     cout<<f[1]<<endl; 
    48     return 0;
    49 } 
    50 void print(){
    51     for(int i=1;i<=n;i++){
    52         cout<<task[i].s<<" "<<task[i].l<<endl;
    53     }
    54     cout<<endl;
    55     for(int i=1;i<=t;i++){
    56         cout<<f[i]<<" ";
    57     }
    58     cout<<endl;
    59 }

    关于上面代码说几点:

     1、第17行 }task[MAXN]; 这样是以无参构造函数初始化了MAXN个变量,已经new出来了。

    写了有参构造函数之后,空构造函数没有了 ,所以写了构造函数没写无参,这里会发生错误

    2、第2,3行常量的定义,const int MAXN=1e4+10;

    3、now这个变量是为了task从上访问到下,所以这是方式可以记一下,很多位置都会用到

    4、变量在知道意思的情况下尽量写的简单点吧

     
    代码:
    1、普通DP
     1 /*
     2 肯定要思路理清楚在写啊
     3 不然只是浪费时间
     4 f[i]表示表示在i~t(t为总时间)的时间内的最大闲暇时间
     5 f[i]初始化为-1
     6 search(int i,int now) 表示i分钟的时候下一条要用到的是now这条记录 
     7 */ 
     8 #include <bits/stdc++.h>
     9 const int INFINITE=0x3fffffff;
    10 const int MAXN=1e4+10;
    11 using namespace std;
    12 struct taskNode{
    13     int s;//start
    14     int l;//last
    15     taskNode(int s,int l){
    16         this->s=s;
    17         this->l=l;
    18     } 
    19     taskNode(){}
    20     bool operator <(const taskNode &p1) const{
    21         if(this->s==p1.s) return this->l>p1.l;
    22         return this->s>p1.s;
    23     }
    24 }task[MAXN];//写了有参构造函数之后,空构造函数没有了 
    25 int f[MAXN],sch[MAXN];
    26 int t,n;
    27 
    28 void print();
    29 void init(){
    30     cin>>t>>n;
    31     for(int i=1;i<=n;i++){
    32         cin>>task[i].s>>task[i].l;
    33         sch[task[i].s]++;
    34     } 
    35     sort(task+1,task+n+1);
    36 }
    37 
    38 void dp(){
    39     memset(f,0,sizeof(f));
    40     for(int i=t,now=1;i>=1;i--){
    41         if(sch[i]==0) f[i]=f[i+1]+1;
    42         else for(int j=1;j<=sch[i];j++,now++){
    43             f[i]=max(f[i],f[i+task[now].l]);
    44         }
    45     }
    46 }
    47 
    48 int main(){
    49     //freopen("in.txt","r",stdin);
    50     init();
    51     
    52     dp();
    53     //print();
    54     cout<<f[1]<<endl; 
    55     return 0;
    56 } 
    57 void print(){
    58     for(int i=1;i<=n;i++){
    59         cout<<task[i].s<<" "<<task[i].l<<endl;
    60     }
    61     cout<<endl;
    62     for(int i=1;i<=t;i++){
    63         cout<<f[i]<<" ";
    64     }
    65     cout<<endl;
    66 }

    最最最主要,写成递推表达式,那么既能DP又能记忆化搜索

    1、第24行, 如果没有无参构造函数,}task[MAXN];会出错,说明这句话是用空构造函数初始化,已经初始化有内存了

    2、写了有参构造函数之后,默认的无参构造函数没有了

    3、结构体里面的符号重载

    4、这个题目为什么从后往前推而不是从前往后推,看上面图,后推前能确保正确,前推后却不一定能

    5、 记忆化递归和DP只是递推公式的运用的方向不一样,其它都是一样的,而且看测试耗时,两个的耗时几乎相同

    6、其实从前往后推还是从后往前推,真正的根源就是递推公式,看递推公式两个公式是不是对的:

    设计状态f【i】表示在i~T(T为总时间)的时间内的最大闲暇时间

    T是固定的,相当于T是起点

    从T反过来思考,所有的问题都迎刃而解

    状态转移方程:

    如果时间点i处没有任务,则f【i】= f【i+1】+1;

    如果时间点i处有任务,则f【i】=max(f【i+任务k的持续时间】); //此处k = 1~时间点i处的任务总个数

    所以很容易发现,想要知道f【i】,必须先求出 f【比i大的时间点】,所以确定本题倒推:由f【T】递推到f【1】。

      

    2、递归

     1 /* 
     2 肯定要思路理清楚在写啊
     3 不然只是浪费时间
     4 f[i]表示表示在i~t(t为总时间)的时间内的最大闲暇时间
     5 f[i]初始化为-1
     6 search(int i,int now) 表示i分钟的时候下一条要用到的是now这条记录
     7 所以最开始search的是(1,1)
     8 递归终止条件:
     9 如果i>t return 0;
    10 */
    11 
    12 
    13 //递归30分 通过1 3 4 三个点,其它都超时 
    14 #include <bits/stdc++.h>
    15 const int INFINITE=0x3fffffff;
    16 const int MAXN=1e4+10;
    17 using namespace std;
    18 vector<int> vct[MAXN];//存结束时间的下一个 
    19 int sch[MAXN]; 
    20 int f[MAXN];
    21 int t,n;
    22 
    23 
    24 void init(){
    25     cin>>t>>n;
    26     for(int i=1;i<=n;i++){
    27         int s,l;
    28         cin>>s>>l;
    29         sch[s]++;
    30         vct[s].push_back(s+l);
    31     }
    32 }
    33 
    34 int search(int i){
    35     if(i>t) return 0;
    36     else if(sch[i]==0){
    37         return search(i+1)+1;
    38     }else if(sch[i]!=0){
    39         int maxv=0;
    40         for(int j=0;j<sch[i];j++){
    41             int tmp=search(vct[i][j]);
    42             if(tmp>maxv) maxv=tmp;
    43         }
    44         //cout<<maxv<<endl;
    45         return maxv;
    46     }
    47 }
    48 
    49 int main(){
    50     //freopen("in.txt","r",stdin);
    51     //freopen("testdata.in","r",stdin);
    52     init();
    53     cout<<search(1)<<endl; 
    54     return 0;
    55 }

    1、肯定要由易到难,先写递归,再写记忆化递归

    2、递归肯定要想清楚了再写,把递推公式想清楚

    3、 递归递归30分, 只能通过1 3 4 三个点,其它都超时 

    4、写递归和写记忆化递归,存储结构发生了变化

    5、这题的暴力是可行的,统计一根根木棒,遇到有新的木棒就把它加上,后面来实现

    3、记忆化递归

     1 /* 
     2 肯定要思路理清楚在写啊
     3 不然只是浪费时间
     4 f[i]表示表示在i~t(t为总时间)的时间内的最大闲暇时间
     5 f[i]初始化为-1
     6 search(int i,int now) 表示i分钟的时候下一条要用到的是now这条记录
     7 所以最开始search的是(1,1)
     8 递归终止条件:
     9 如果i>t return 0;
    10 */
    11 
    12 
    13 //记忆化递归和dp只是方向不同罢了 
    14 //由浅入深,有递归到记忆化递归
    15 //不管是dp还是递归,都要把递推方程找出来,也就是都要知道怎么解
    16 //因为递归和dp只是解的形势不同而已 
    17 #include <bits/stdc++.h>
    18 const int INFINITE=0x3fffffff;
    19 const int MAXN=1e4+10;
    20 using namespace std;
    21 vector<int> vct[MAXN];//存结束时间的下一个 
    22 int sch[MAXN]; 
    23 int f[MAXN];
    24 int t,n;
    25 
    26 
    27 void init(){
    28     cin>>t>>n;
    29     for(int i=1;i<=n;i++){
    30         int s,l;
    31         cin>>s>>l;
    32         sch[s]++;
    33         vct[s].push_back(s+l);
    34     }
    35     memset(f,-1,sizeof(f)); 
    36 }
    37 
    38 int search(int i){
    39     if(i>t) return 0;
    40     else if(sch[i]==0){
    41         if(f[i+1]!=-1) return f[i]=f[i+1]+1;
    42         return f[i]=search(i+1)+1;
    43     }else if(sch[i]!=0){
    44         int maxv=0;
    45         for(int j=0;j<sch[i];j++){
    46             //这句话有问题 ,只记忆化上面的不记忆化这部分,只有6号点一个点超时 
    47             //if(f[vct[i][j]]!=-1) return f[i]=f[vct[i][j]];
    48             //这样就都过了 
    49             int tmp;
    50             if(f[vct[i][j]]!=-1) tmp=f[vct[i][j]];
    51             else tmp=search(vct[i][j]);
    52             if(tmp>maxv) maxv=tmp;
    53         }
    54         return f[i]=maxv;
    55     }
    56 }
    57 
    58 int main(){
    59     //freopen("in.txt","r",stdin);
    60     //freopen("testdata.in","r",stdin);
    61     init();
    62     cout<<search(1)<<endl; 
    63     return 0;
    64 }

    1、肯定要由易到难,先写递归,再写记忆化递归

    2、递归肯定要想清楚了再写,把递推公式想清楚

    3、看上面的AC情况图,记忆化递推和DP的时间耗时是一样的

    4、第46,47行,我们只部分记忆化的话,可以过9个点

    5、memset(f,-1,sizeof(f)); 

     
     纯暴力模拟,30分 ,可以了 
     1 /*
     2 纯暴力模拟,30分 ,可以了 
     3 */
     4 #include <bits/stdc++.h>
     5 const int N=1e4+10;
     6 using namespace std;
     7 int n,k,f[N],num,ans=0;
     8 vector<int> vct[N];
     9 
    10 void search(int s){
    11     if(s>n){if(num>ans) ans=num; return ;}
    12     if(vct[s].empty()){ num++,search(s+1);num--;}
    13     else for(int i=0;i<vct[s].size();i++){
    14         int l=vct[s][i];
    15         search(s+l);
    16     }
    17 }
    18 
    19 int main(){
    20     //freopen("in.txt","r",stdin);
    21     cin>>n>>k;
    22     for(int i=1;i<=k;i++){ int s,l; cin>>s>>l; vct[s].push_back(l);}
    23     search(1);
    24     cout<<ans<<endl;
    25     return 0;
    26 } 
     
  • 相关阅读:
    安装node.js webkit环境[一]
    wpf 窗口最小化后,触发某事件弹出最小化窗口并置顶
    c# 旋转图片 无GDI+一般性错误
    类库里面添加日志记录 log4net
    string转xml
    DES c#加密后java解密
    使用排序字典排序
    怎么让一段xml被识别为字符串
    新装iis 页面503错误 DefaultAppPool停止解决方案
    hession
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7535289.html
Copyright © 2011-2022 走看看