http://poj.org/problem?id=1821
当我们在考虑内层循环j以及决策k的时候,我们可以把外层变量i看作定值,以此来优化dp状态转移方程。
题意 有n个工人准备铺m个连续的墙,每个工人有他必须图的一面墙壁Si,最多连续铺Li,每铺一个就花费Ci的钱,问最多要多少钱;
朴素算法很好想,就dp[i][j]维护i工人到这j层墙壁的最大值,对于每个工人去枚举他涂墙壁的开头和结尾然后更新即可。
时间复杂度O(NMM) M的范围是16000,很显然会T,我们考虑状态转移方程。
对于每个工人,dp[i][j]的更新是寻找一个k使得dp[i - 1][k - 1] + (j - k + 1 ) * P 最大;
在这个转移方程里,我们将i看作定值,除了状态变量j之外还有一个决策j,看似很难处理,我们将方程变形.
dp[i][j]的更新变为 max(dp[i - 1][k - 1] - (k - 1) * P) + j * P;
在这一层中,最大值的寻找仅和k有关,而k事实上对每一个i都是可以预处理出来的,在j查询的时候只有范围变动,问题就变成了常规的优化区间最大值的问题。
这里附上用ST表优化和单调队列优化的两种方法。
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 110; const int maxm = 20010; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,tmp,K; inline int read() { int now=0;register char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-‘0‘,c=getchar()); return now; } struct Node{ int L,P,S; }node[maxn]; int dp[2][maxm]; bool cmp(Node a,Node b){ return a.S < b.S; } int DP[maxm][20]; int mm[maxm]; int num[maxm]; void initRMQ(int n,int b[]){ mm[0] = -1; for(int i = 1; i <= n ; i ++){ mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1:mm[i - 1]; DP[i][0] = b[i]; } for(int j = 1; j <= mm[n]; j ++){ for(int i = 1; i + (1 << j) - 1 <= n ; i++){ DP[i][j] = max(DP[i][j - 1],DP[i + (1 << (j - 1))][j - 1]); } } } int rmq(int x,int y){ int k = mm[y - x + 1]; return max(DP[x][k],DP[y - (1 << k) + 1][k]); } int main() { while(~Sca2(N,K)){ For(i,1,K){ scanf("%d%d%d",&node[i].L,&node[i].P,&node[i].S); } sort(node + 1,node + 1 + K,cmp); Mem(dp,0); For(i,1,K){ Mem(num,0); Mem(dp[i & 1],0); for(int k = max(node[i].S - node[i].L + 1,1); k <= node[i].S; k ++){ num[k] = dp[i - 1 & 1][k - 1] - node[i].P * (k - 1); } initRMQ(node[i].S,num); For(j,1,N){ dp[i & 1][j] = max(dp[i - 1 & 1][j],dp[i & 1][j - 1]); if(j >= node[i].S && j <= node[i].S + node[i].L - 1){ dp[i & 1][j] = max(dp[i & 1][j],rmq(max(j - node[i].L + 1,1),node[i].S) + node[i].P * j); } } } Pri(dp[K & 1][N]); } #ifdef VSCode system("pause"); #endif return 0; }
ST表优化
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 110; const int maxm = 20010; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,tmp,K; inline int read() { int now=0;register char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-‘0‘,c=getchar()); return now; } struct Node{ int L,P,S; }node[maxn]; int dp[2][maxm]; bool cmp(Node a,Node b){ return a.S < b.S; } int Queue[maxm]; int head,tail; int main() { while(~Sca2(N,K)){ For(i,1,K){ scanf("%d%d%d",&node[i].L,&node[i].P,&node[i].S); } sort(node + 1,node + 1 + K,cmp); Mem(dp,0); For(i,1,K){ head = 1; tail = 0; Mem(dp[i & 1],0); for(int k = max(node[i].S - node[i].L + 1,1); k <= node[i].S; k ++){ int ans = dp[i - 1 & 1][k - 1] - node[i].P * (k - 1); while(head <= tail && dp[i - 1 & 1][Queue[tail] - 1] - node[i].P * (Queue[tail] - 1)<= ans) tail--; Queue[++tail] = k; } For(j,1,N){ dp[i & 1][j] = max(dp[i - 1 & 1][j],dp[i & 1][j - 1]); if(j >= node[i].S){ while(head <= tail && Queue[head] < j - node[i].L + 1) head++; if(head <= tail) dp[i & 1][j] = max(dp[i & 1][j],dp[i - 1 & 1][Queue[head] - 1] + (j - Queue[head] + 1) * node[i].P); } } } Pri(dp[K & 1][N]); } #ifdef VSCode system("pause"); #endif return 0; }
单调队列优化
值得一提的是单调队列的查询和处理的时间都是线性的,总时间复杂度为O(NM),而ST表的预处理要用到nlnn,所以用时会比ST表快一些
原文地址:https://www.cnblogs.com/Hugh-Locke/p/9643832.html
时间: 2024-11-08 21:51:16