题目描述
尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。
尼克的一个工作日为 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;
}