Task Schedule
题目链接:Click Here~
题目解析:
给你N任务,M台机器。每个任务都有开始的时间和结束的时间以及要完成这项任务所要花费的时间。问你是否可以在规定的时间内完成所有的任务。
算法分析:
我的做法是网络流,求出最大流。而在网上看到别人似乎可以用贪心过的。然后,一试果然是可以的。我发现这种题还有一种类型就是给你N个任务M台机器要你在最短的时间内求出。下面还会给出这种类型的,这样就可以很好的进行对比学习了。
建模分析:
我们可以想一想有N个任务,M台机器。是不是就说明每天至多可以完成M项任务?而且每个任务都有自己的开始时间和结束时间。换句话说,每个任务每天只要在规定的时间内都可以被完成一次。所以,我们从中想到了建图的思路。即,建立两个源点和汇点。把每个任务跟源点相连,流量为所需要的天数。而任务跟规定的时间相连,流量为1,因为一个任务每天只能被完成一次。而每天跟汇点相连,流量为M。因为,每天至多可以完成M项任务。
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = ~0U >> 2;
const int MAXN = 1e3 + 10;
struct Edge{
int from,to,cap,flow;
Edge(int f,int t,int c,int _f)
:from(f),to(t),cap(c),flow(_f){};
};
class MaxFlow{
public:
void Init();
void AddEdge(int form,int to,int cap);
bool BFS();
int DFS(int u,int a);
int Maxflow();
bool Solve(int n,int m);
private:
vector<Edge> edges;
vector<int> G[MAXN];
int S,T,N,M;
int cur[MAXN],d[MAXN];
bool vst[MAXN];
};
void MaxFlow::Init()
{
for(int i = 0;i < MAXN;++i)
G[i].clear();
edges.clear();
}
inline void MaxFlow::AddEdge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
int sz = edges.size();
G[from].push_back(sz-2);
G[to].push_back(sz-1);
}
bool MaxFlow::BFS()
{
memset(vst,false,sizeof(vst));
vst[S] = true; d[S] = 0;
queue<int> Q;
Q.push(S);
while(!Q.empty()){
int u = Q.front();
Q.pop();
for(int i = 0;i < (int)G[u].size();++i){
Edge& e = edges[G[u][i]];
if(!vst[e.to]&&e.cap > e.flow){
d[e.to] = d[u] + 1;
vst[e.to] = true;
Q.push(e.to);
}
}
}
return vst[T];
}
int MaxFlow::DFS(int u,int a)
{
if(u==T||a==0)
return a;
int f,flow = 0;
for(int& i = cur[u];i < (int)G[u].size();++i){
Edge& e = edges[G[u][i]];
if(d[e.to]==d[u]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow += f;
edges[G[u][i]^1].flow -= f;
flow += f;
a -= f;
if(a==0)break;
}
}
return flow;
}
int MaxFlow::Maxflow()
{
int flow = 0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow += DFS(S,INF);
}
return flow;
}
bool MaxFlow::Solve(int n,int m)
{
Init();
int p,s,e,sum = 0,MAXD = 0;
S = 0; N = n; M = m;
for(int i = 1;i <= N;++i){
scanf("%d%d%d",&p,&s,&e);
sum += p;
MAXD = max(MAXD,e);
AddEdge(S,i,p);
for(int j = s;j <= e;++j)
AddEdge(i,j+N,1);
}
T = N+M+1;
for(int i = 1+N;i <= MAXD+N;++i)
AddEdge(i,T,M);
int maxflow = Maxflow();
return sum == maxflow;
}
MaxFlow mf;
int main()
{
// freopen("Input.txt","r",stdin);
int T,n,m;
scanf("%d",&T);
for(int kase = 1;kase <= T;kase++){
scanf("%d%d",&n,&m);
if(mf.Solve(n,m))
printf("Case %d: Yes\n",kase);
else
printf("Case %d: No\n",kase);
puts("");
}
return 0;
}
贪心的思路:
我们可以根据贪心的思想,每一天都去开始时间最近的,即,最早开始的。如果,最早开始的多于M个怎么办?
其实这时候要再一次运用贪心思想,先完成要完成该项任务剩余时间少的。最后根据进行模拟,而模拟过程我们又可以运用优先队列来很好的帮助我们完成。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 500+10;
struct Node{
int s,e,p,x;
bool operator<(const Node &rhx)const{
return s != rhx.s ? s > rhx.s:x > rhx.x;
}
};
int main()
{
// freopen("Input.txt","r",stdin);
int T,N,M;
scanf("%d",&T);
for(int kase = 1;kase <= T;kase++)
{
bool flag = true;
int i,j,s,e,p,MAXD = 0;
Node tmp;
priority_queue<Node> Q;
scanf("%d%d",&N,&M);
for(i = 1;i <= N;++i){
scanf("%d%d%d",&p,&s,&e);
e++;
MAXD = max(MAXD,e);
if(s+p<=e){
tmp.s = s;
tmp.e = e;
tmp.p = p;
tmp.x = e-(s+p);
Q.push(tmp);
}
else
flag = false;
}
printf("Case %d: ",kase);
if(!flag){
puts("No");
puts("");
continue;
}
//模拟每天M台机器工作的过程
for(i = 1;i < MAXD;++i){
bool sub = true,out = false;
for(j = 1;j <= M;++j){
if(Q.empty()){ //所有工作已经完成
out = true;
break;
}
tmp = Q.top();
if(tmp.s > i){
sub = false;
break;
}
Q.pop();
tmp.p--;
tmp.s = i+1;
tmp.x = tmp.e-(tmp.s+tmp.p);
if(tmp.x < 0){
out = true;
flag = false;
break;
}
if(tmp.p==0)
N--;
else
Q.push(tmp);
}
//更新优先队列中工作起始时间
for(;j<=N&&!out&⊂++j){
tmp = Q.top();
if(tmp.s > i)
break;
Q.pop();
tmp.s = i+1; // 更新起始时间
tmp.x = tmp.e-(tmp.s+tmp.p);
if(tmp.x < 0){
out = true;
flag = false;
break;
}
Q.push(tmp);
}
if(out)
break;
}
if(flag)
puts("Yes");
else
puts("No");
puts("");
}
return 0;
}
题目链接:Click Here~
题目分析:
这是一道浙江省的省赛题。就是给你N个要烹饪的东西,M口锅。每口锅每次只能烹饪一种东西,问你最短的时间烹饪完所有的东西要多久。
算法分析:
我也不知道这题确切考查的是啥。我只知道我这跟腾讯马拉松的一道题一样的。所以当时比赛的时候我就水了。
我一开始是用优先队列模拟的,结果超时了。后来直接换成了公式,直接算,就Y了。具体思路是这样的,我们可以想到每种菜都要x天。而总共要进行的次数我们假设为SUM,则平均我们要烹饪SUM/M次。但是有两种情况是特殊的,就是其中有某道菜的天数X > SUM/M,这时候就有可能到最后的时候,就剩下这一道菜了,而锅剩下了,所以平均值这时候显然是不对的,所以,我们结果要取X。还有就是N <= M,就是无论在什么情况下没一次每一道菜都保证可以被烹饪到,所以,此时我们只要去最大的次数就好了。其他的就是平均值了。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
// freopen("In.txt","r",stdin);
int T,n,m,v;
scanf("%d",&T);
while(T--)
{
int tim = 0,big = 0;
scanf("%d%d",&n,&m);
for(int i = 0;i < n;++i){
scanf("%d",&v);
tim += v;
big = max(big,v); //zhao dao zui da shi jian
}
if(n <= m){
printf("%d\n",big);
continue;
}
int ans = tim / m;
if(tim%m)
ans++;
if(ans < big) //qizhong de dan ge hai geng da
printf("%d\n",big);
else
printf("%d\n",ans);
}
return 0;
}