题目:http://acm.hdu.edu.cn/showproblem.php?pid=5501
贪心+动态规划,贪心确定做题顺序,动态规划选定做哪些题目。
对于任意一个做题序列x1,x2,。。。,xm,对于该序列中任意的相邻两项xi,xi+1,交换这相邻的两项,只会对这两道题的得分有影响,对其余的题目不会产生影响。
设在时刻t,分值为X,开始做题xi,xi+1,则分值变为X1=X+[Ai-Bi*(Ci+t)]+[Ai+1-Bi+1*(Ci+Ci+1+t)];若做题xi+1,xi,则分值为X2=X+[Ai+1-Bi+1*(Ci+1+t)]+[Ai-Bi*(Ci+Ci+1+t)]
,若X1<X2,则需要交换这两项,即BiCi+1 >Bi+1Ci,等价于Bi/Ci > Bi+1/Ci+1。所以对于一个确定的题目集合,做题的最优顺序只与每道题目的Bi/Ci有关,按每道题目的B/C排序,按照比值由大到小做题。
然后就是按照排好的顺序,选择做哪些题目,即0/1背包,状态转移方程也同0/1背包。
#include<cstdio> #include<cstdlib> #include<vector> #include<cstring> #include<set> #include<queue> #include<stack> #include<map> #include<algorithm> #include<iostream> #include<ctime> #include<bitset> using namespace std; #define N 10005 #define NMAX 2000000000 typedef long long ll; struct type{ int a; int b; int c; double f; }p[N]; int dp[N*3]; bool cmp(const type& x, const type& y){ return x.f > y.f; } int main(){ int T, n, m, t; while ( scanf("%d", &T) != EOF){ while ( T > 0 ){ T--; scanf("%d %d", &n, &t); for ( int i = 0; i < n; i++ ){ scanf("%d %d %d", &p[i].a, &p[i].b, &p[i].c); p[i].f = (double)p[i].b / p[i].c; } sort(p, p+n, cmp); memset(dp, -1, sizeof(dp)); dp[0] = 0; for ( int i = 0; i < n; i++ ){ for ( int j = t; j >= p[i].c; j-- ){ if ( dp[j-p[i].c] == -1)continue; if ( dp[j-p[i].c]+p[i].a-(j*p[i].b) > dp[j] ) dp[j] = dp[j-p[i].c]+p[i].a-(j*p[i].b); } } m = 0; for ( int i = 0; i <= t; i++ ){ if ( dp[i] > m ) m = dp[i]; } cout << m << endl; } } return 0; }