fafu 1231 dp(线段树优化dp)
http://acm.fafu.edu.cn/problem.php?id=1231
这题是说在 n 个时间单位内工作产生不同的价值
但在每个单位时间工作后都有限定接下去几个单位时间不能再工作
和 接下去几个单位时间内必须要再次工作
这题可以从前往后推也可以从后往前推,但从前往后推必须
两重循环(我还不会优化这种情况)
从前往后推,TLE
//fafu 1231 //这个代码是从前往后推(很暴力),会TLE的 #include <stdio.h> #include <string.h> #define N 50005 int n; int dp[N], val[N], no_work[N], work[N]; int max(int a, int b) { return a > b ? a : b; } int main() { freopen("in.txt", "r", stdin); int n_case; scanf("%d", &n_case); while(n_case--) { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d%d", &val[i], &no_work[i], &work[i]); int max = 0; for(int i = 1; i <= n; ++i)//从前往后推 { dp[i] = val[i]; //第i 个时间单位要工作,就要 在i 之前的单位时间 //里寻找 单位时间j 工作的最大价值加上 i 的价值 //才能使得第 i 个时间单位工作后得到的价值最大 for(int j = i; j > 0; --j) { //若j 工作后,i刚好在 j限定的不能工作和必须工作之间,并且j 时 //的价值加上i 的价值比 i 目前的价值大 if(i-j >= no_work[j] && i-j < work[j] && dp[i] < dp[j] + val[i]) { dp[i] = dp[j] + val[i]; if(max < dp[i]) max = dp[i]; } } } printf("%d\n", max); } return 0; }
从后往前推(TLE)
//fafu 1231 //从后往前推,稍微优化,还是TLE //不过有些人还是AC 了 #include <stdio.h> #include <string.h> #define N 50005 int n; int dp[N], val[N], no_work[N], work[N], tree[N*4]; int max(int a, int b) { return a > b ? a : b; } int main() { int n_case; scanf("%d", &n_case); while(n_case--) { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d%d", &val[i], &no_work[i], &work[i]); int tmax, m = 0; for(int i = n; i > 0; --i) //从后往前推 { dp[i] = val[i]; tmax = 0; //若第i 个单位时间要工作,则找 i 后第几个时间单位 第几个单位 //时间继续工作 能得到最大价值 for(int j = i+no_work[i]; j <= n && j < i+work[i]; ++j) { tmax = max(tmax, dp[j]); } dp[i] += tmax; //更新 最大价值 if(m < dp[i]) m = dp[i]; } printf("%d\n", m); } return 0; }
线段树优化
//fafu 1231 dp(线段树优化dp) //具体看一下代码 #include <stdio.h> #include <string.h> #define N 50005 int n; int dp[N], val[N], no_work[N], work[N], tree[N*4]; int max(int a, int b) { return a > b ? a : b; } void build_tree(int l, int r, int root) { if(l == r) { tree[root] = val[l]; return; } int mid = (l + r) >> 1; build_tree(l, mid, root * 2); build_tree(mid + 1, r, root * 2 + 1); tree[root] = max(tree[root*2], tree[root*2+1]); } //询问,返回最大值 int query(int l, int r, int ll, int rr, int root) { if(l == r || (l == ll && r == rr)) return tree[root]; int mid = (l + r) >> 1; if(mid < ll) return query(mid+1, r, ll, rr, root*2+1); else if(mid >= rr) return query(l, mid, ll, rr, root*2); else return max(query(l, mid, ll, mid, root*2), query(mid+1, r, mid+1, rr, root*2+1)); } void update(int l, int r, int ll, int root) { if(l == r) { tree[root] = dp[l]; return; } int mid = (l + r) >> 1; if(mid < ll) update(mid+1, r, ll, root*2+1); else if(mid >= ll) update(l, mid, ll, root*2); tree[root] = max(tree[root*2], tree[root*2+1]); } int main() { int n_case; scanf("%d", &n_case); while(n_case--) { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d%d", &val[i], &no_work[i], &work[i]); build_tree(1, n, 1); //建树,根节点保持最大值 for(int i = n; i > 0; --i)//从后往前推 { dp[i] = val[i]; int tmp = i + work[i] - 1; //tmp单位时间内必须再次工作 if(tmp > n) //若tmp超过 最大单位时间则等于最大单位时间 tmp = n; //i+no_work[i]-1 表示这时间内不能再工作 //这里要注意 这个时间要比 tmp 早 if(i + no_work[i] -1 < tmp && tmp != i) { //若i 要工作,找i 之后不能工作 和 必须工作 这 //两段时间内价值最大的 单位时间 作为 i 的下一个工作单位时间 dp[i] += query(1, n, i+no_work[i], tmp, 1); update(1, n, i, 1); //更新根节点 } } printf("%d\n", tree[1]); } return 0; }
树状数组优化
////fafu 1231 dp(树状数组优化dp) #include <stdio.h> #include <string.h> #define N 50005 int n; int tree[N], val[N], no_work[N], work[N]; int max(int a, int b) { return a > b ? a : b; } int lowbit(int root) //求二进制最低位的 1 { return root & (root^(root-1)); //等价于 root & (-root),不知道为什么 } void build_tree(int root) { int d = root - lowbit(root), m = val[root]; for(int i = d+1; i < root; ++i) m = max(m, val[i]); tree[root] = m; } int find_max(int l, int r) //找段内最大值 { int ans = val[r]; while(1) { ans = max(ans, val[r]); if(l == r) break; //for 的意思是:若 r-l >= lowbit(r) 表示 tree[r]保存是从 r-lowbit(r)+1 到tree[r] //的最大值而 l < r-lowbit(r) +1,说明还要比较从 l到 r-lowbit(r)的最大值 //若 r-l >= lowbit(r)不成立则 r-lowbit(r)+1 小于l,因此先比较 //单个数的值(val[r]) 和 ans(记录最大值) 的大小,即ans = max(val[r], num[r]) //然后在比较 l 到 r-1 的最大值(即以下这个for) for(r -= 1; r - l >= lowbit(r); r -= lowbit(r)) { ans = max(ans, tree[r]); } } return ans; } void update(int root) { //和 root最低位1 以下的位为都为0 一样的数保存 //的最大值范围都包括 val[root] int tmp = val[root];//先取最低位1 的位置 while(root <= n) { tree[root] = max(tree[root], tmp); root += lowbit(root); //相当于把最低位1 加1 变成0,进1位(相当于在后面加0) } } int main() { int n_case; scanf("%d", &n_case); while(n_case--) { scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d%d%d", &val[i], &no_work[i], &work[i]); build_tree(i); } int ans = 0; for(int i = n; i > 0; --i) { int tmp = i + work[i] - 1; //记录那个单位时间必须工作 if(tmp > n) //若大于 最大时间单位,则令其等于最大单位时间 tmp = n; if(i + no_work[i] <= tmp) { val[i] += find_max(i+no_work[i], tmp); update(i); } ans = max(ans, val[i]); } printf("%d\n", ans); } return 0; }