这个题目给定N航班的发出时间和结束时间以及价值,要求不冲突时间的最大价值
第一时间想到经典的N方DP,即对航班按发出时间排一下序之后每个i对前面的都扫一遍
时间过不了N有10万,只能想优化了,一开始想了个用树状数组记录每次加入某个航班之后 后面的所有在结束时间后的区间全部可以加一个最大值,但是首先时间区间的量更大,而且树状数组不好这么操作
后来实在没见过这种DP,还是看的别人的算法,还是挺叼的,不过其实还是沿用的背包思想
对于每个航班区间,我只需要考虑取或者不取,这样的话,对航班按出发时间S排序,从后往前,对该航班结束时间T找到 i+1到n中满足出发时间Ti<=Sk(i<k<=n)的,由于找到的已经是最优值的,所以直接+现在的价值,当然,如果不取该区间的话 dp[i]=dp[i+1]即可.用了典型的01背包思想。因为查找可以用二分,所以把时间复杂度控制在NlogN,不会超时
当然,为什么要从后往前扫值得深思,首先这样扫过来肯定是对的,因为他的子结构只会在他后面(即满足结束时间《=发出时间的),而且他们已经是最优了。反过来如果从前面开始扫,那他们的子结构还是在后面,但后面的还没访问过,不是最优值,所以无法转移。因此这就是为什么转移是这样的
这个题目真的不错,DP模型很经典,但是状态转移很新颖
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct node { int s,d,p; bool operator < (const node& rhs) const{ return s<rhs.s; } }rent[10010]; const int N = 10010; int dp[N]; int bs(int v,int L,int R) { int mid; while (L<R){ mid=(L+R)>>1; if (v<=rent[mid].s) R=mid; else L=mid+1; } return L; } int main() { int t; scanf("%d",&t); while(t--) { memset(dp,0,sizeof dp); int n; scanf("%d",&n); for (int i=0;i<n;i++){ scanf("%d%d%d",&rent[i].s,&rent[i].d,&rent[i].p); rent[i].d=rent[i].s+rent[i].d; } sort(rent,rent+n); memset(dp,0,sizeof dp); for (int i=n-1;i>=0;i--){ dp[i]=dp[i+1]; int loc=bs(rent[i].d,i+1,n); dp[i]=max(dp[i],dp[loc]+rent[i].p); } printf("%d\n",dp[0]); } return 0; }
SPOJ RENT 01背包的活用+二分
时间: 2024-11-04 20:42:56