工作安排小K
[]测试连接](https://www.acwing.com/problem/content/description/5840/)
小 K 有 N 项工作等待完成,第 i 项工作需要花 ti 单位时间,必须在 di 时刻或之前完成,报酬为 pi。
假设小 K 工作时刻从 0 开始,且同一时刻只能做一项工作、工作一旦开始则不可中断或切换至其他工作,请你帮小 K 规划一下如何选择合适的工作,使小 K 可以获得最多的报酬。
输入格式
输入第一行是一个正整数 T,表示数据的组数。
接下来有 T 组数据,每组数据第一行是一个正整数 N,表示待完成工作的数量。
接下来的 N 行,每行三个非负整数 ti,di,pi,表示第 i 项工作需要花费的时间、截止时间以及报酬。
输出格式
对于每组数据,输出小 KK 能获得最多的报酬是多少。
首先是递归版本:
按照贪心考虑的话就是先考虑最早结束的,因为结束了就没办法做了,如果按照最晚开始考虑的话,会出现,虽然工作A的"最晚开始时间"早于工作B,但A的截止时间反而晚于B,这会导致决策冲突,例如A:7 10和B:3 8
如果按照最晚开始时间的话A现做,做完之后B就不可以做了,但是如果按照最早截止时间的话,B做完了A还可以做呢.所以考虑按照最早截止时间考虑,对于每件事,考虑做和不做所得到的收益:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int read()
{int f = 0, sum = 0;char ch = getchar();while (!isdigit(ch)) f |= ch == '-', ch = getchar();while (isdigit(ch)) sum = sum * 10 + (ch ^ 48), ch = getchar();return f ? -sum : sum;
}
#define Len 100003
typedef pair<int,int> PII;
struct game
{int t,d,p;
};
int ans;
int n;
bool mcmp(game&a,game&b)
{return a.d<b.d;
}
void dfs(int index,int time,int sum,game arr[])
{if(index==n){ans=max(ans,sum);return;}dfs(index+1,time,sum,arr);if(time<=arr[index].start)dfs(index+1,time+arr[index].t,sum+arr[index].p,arr);}
int main()
{int ci;cin>>ci;while(ci--){cin>>n;game arr[n+2];for(int i=0;i<n;i++){cin>>arr[i].t>>arr[i].d>>arr[i].p;}sort(arr,arr+n,mcmp);ans=0;dfs(0,0,0,arr);cout<<ans<<endl;}return 0;
}
从上面的思考你是不是想到了01背包对于每件物品考虑要和不要所得到的收益,所以下面给出动态规划的版本:
定义dp[i][j],
截止到时间为考虑前 i i i个工作截止时间为j
的时候所能得到最大的时间状态转移方程如下:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − c o s t [ i ] ] + v a l [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i][j-cost[i]]+val[i]) dp[i][j]=max(dp[i−1][j],dp[i][j−cost[i]]+val[i])
可以看出当前状态只是依赖于以前的转态,所以可以使用一个一维数组来更新下一个的数组;
转态转移方程可以转化为:
d p [ j ] = m a x ( d p [ j ] , d p [ j − c o s t [ i ] . t ] + v a l [ i ] . p ) ; dp[j] = max(dp[j], dp[j - cost[i].t] + val[i].p); dp[j]=max(dp[j],dp[j−cost[i].t]+val[i].p);
也可以进行空间压缩.这里使用倒序遍历来避免同一个状态被多次使用(一个工作只能做一次)
代码如下:
#pragma GCC optimize(2)
#include <bits/stdc++.h>using namespace std;const int N = 5010;struct Node
{int t, d, p; // 消耗时间, 必须在d时刻完成,报酬bool operator<(const Node &a) const{if (a.d != d)return d < a.d;else if (a.t != t)return t < a.t;}
} a[N];int n, m;
int f[N];void solve()
{memset(f, 0, sizeof(f));cin >> n;for (int i = 1; i <= n; i++){int t, d, p;cin >> t >> d >> p;a[i].t = t;a[i].d = d;a[i].p = p;}sort(a + 1, a + n + 1);for (int i = 1; i <= n; i++){for (int j = a[i].d; j >= a[i].t; j--){f[j] = max(f[j], f[j - a[i].t] + a[i].p);}}int ans = 0;for (int i = 0; i < N; i++){ans = max(ans, f[i]);}cout << ans << '\n';
}int main()
{ios::sync_with_stdio(false);cin.tie(0);int T;cin >> T;while (T-- > 0){solve();}return 0;
}
对了如果你的算法已经可以比较满足题目的要求了,但是还是超时,可以试一试加上#pragma optimize(2)
来碰一碰运气,说不定就过了.