尼克的任务

题目描述

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

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

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

输入格式

输入数据第一行含两个用空格隔开的整数 n 和 k。

接下来共有 k 行,每一行有两个用空格隔开的整数 p 和 t,表示该任务从第 p 分钟开始,持续时间为 t 分钟。

输出格式

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

输入输出样例

输入

15 6

1 2

1 6

4 11

8 5

8 1

11 5

输出

4

说明/提示

数据规模与约定

  • 对于 100%100% 的数据,保证 1≤n≤104,1≤k≤104,1≤p≤n,1≤p+t−1≤n

解法一:最短路

将每一分钟看作图上的一个点,一个从a分钟开始持续b分钟的任务看做一条点a到点a+b权重为b的边,最后将没有出度的边添加一条到下一分钟权重为0的边,再跑一边最短路,求出工作的最短时间

最后要求的时间是到n+1分钟

堆优化版的dijkstra

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
const int M=1e4+10,N=2*M;

int n,k;
int h[N],ne[N],w[N],e[N],idx;
bool st[N],s[N];
int dist[N];

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
    priority_queue<PII,vector<PII>,greater<PII>> q;
    memset(dist,0x3f3f3f,sizeof dist);
    dist[1]=0;
    q.push({0,1}); //distance=0,id=1;
    while(q.size())
    {
        auto t=q.top();
        q.pop();
        int ver=t.second,distance=t.first;
        if(st[ver]) continue;
        st[ver]=true;
        
        for(int i=h[ver];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>distance+w[i])
            {
                dist[j]=distance+w[i];   
                q.push({dist[j],j});
            }
        }
    }
    
    return dist[n+1];
}
int main()
{
    cin>>n>>k;
    
    memset(h,-1,sizeof h);
    for(int i=1;i<=k;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,a+b,b);
        s[a]=true;
    }
    for(int i=1;i<=n;i++)   
    {
        if(!s[i])   add(i,i+1,0);
    }
    
    int t=dijkstra();
    
    cout<<n-t;
    return 0;
}

spfa算法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
const int M=1e4+10,N=2*M;

int n,k;
int h[N],ne[N],w[N],e[N],idx;
bool st[N],s[N];
int dist[N];
int q[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()  // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
{
    int hh = 0, tt = 0;
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    q[tt ++ ] = 1;
    st[1] = true;

    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
                {
                    q[tt ++ ] = j;
                    if (tt == N) tt = 0;
                    st[j] = true;
                }
            }
        }
    }

    if (dist[n+1] == 0x3f3f3f3f) return -1;
    return dist[n+1];
}

int main()
{
    cin>>n>>k;
    
    memset(h,-1,sizeof h);
    for(int i=1;i<=k;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,a+b,b);
    }
    for(int i=1;i<=n;i++)   
    {
        if(h[i]==-1)   add(i,i+1,0);
    }
    
    int t=spfa();
    
    cout<<n-t;
    return 0;
}

解法二:动态规划

f[ ]:表示从第i分钟到第n分钟的空闲时间

if(当前时间没有任务)        

        f[i]=f[i+1】+1

else

        f[i]=当前时间的所有任务中选一个完成后的时刻到最后的空闲时间

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N=1e4+10;
int n,k;
int f[N];
vector<int> v[N];

int main()
{
    cin>>n>>k;
    for(int i=1;i<=k;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        v[a].push_back(b);
    }
    
    for(int i=n;i>=1;i--)
    {
        if(v[i].size()==0)    f[i]=f[i+1]+1;
        else
        {
            for(auto item:v[i])
            {
                f[i]=max(f[i+item],f[i]);
            }
        }
    }
    
    cout<<f[1];
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑夜蔓蔓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值