poj1821——Fence
题意:
一个栅栏一共有n(从1——n)个木板,我们找k个工人去粉刷它,li表示每个人有限制粉刷木板数量,pi表示粉刷一个木板得到的钱,si表示他开始在那个木板前面
如果一个工人要粉刷,那么他必须粉刷si这个木板,而且工人粉刷时必须是连续的木板
题解:
dp[i][j]表示有i个人粉刷j块木板所获得的最大利润
dp[i][j]=max(max(dp[i-1][j],dp[i][j-1]),dp[i][k]+(j-k)*p(i))
dp[i-1][j]表示i-1个人粉刷j块木板所获得的最大利润
dp[i][j-1]表示i个人粉刷j-1块木板所获得的最大利润
dp[i][k]+(j-k)*p(i) 这里面的k是枚举在第i个人可以粉刷木板的数量(因为题目要求第i个人必须粉刷si这块木板,那么粉刷区间肯定也包括它)
//m是一个结构体,里面包含一个工人的li,pi,si for(int j = m[i].s;j <= m[i].s + m[i].l - 1;j ++) { for(int k = j - m[i].l;k <= m[i].s - 1;k ++) if(k >= 0) { dp[i][j] = max(dp[i][j],dp[i - 1][k] + (j - k) * m[i].p); } }
话可以利用单调队列降低复杂度
通过上面的代码我们可以看出来j越大,那么k也就随之变大,这就符合单调队列的特性,单调队列(递减队列)里面放dp[i][k]-k*p(i),为什么放这个是因为我们后面枚举j的时候
直接可以通过 (队列头)+j*p(i) 来找求最优解(这个(j*p(i))与前面的抵消了一部分,剩下的就是第i个人粉刷的部分),因为递减队列头部放的值肯定是最大的,所以
队列头部就是最好的dp转移位置,为了防止粉刷区间大于l(i),我们要每次先对单调队列中的数据进行处理
代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<deque> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=2e5+10; //数组开到2e4都不行, 10 const int mod = 998244353; 11 const int INF=0x3f3f3f3f; 12 struct shudui 13 { 14 int l,p,s; 15 }m[maxn]; 16 bool mmp(shudui x,shudui y) 17 { 18 return x.s<y.s; 19 } 20 int dp[110][maxn]; 21 struct jihe 22 { 23 int k,x; 24 }str1; 25 deque<jihe>r; 26 int main() 27 { 28 int n,k; 29 scanf("%d%d",&k,&n); 30 for(int i = 1;i <= n;i ++) 31 scanf("%d%d%d",&m[i].l,&m[i].p,&m[i].s); 32 sort(m + 1,m + 1 + n,mmp); //这个要加上 33 34 for(int i=1;i<=n;++i) 35 { 36 for(int j = 1;j <= k;j ++) 37 dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]); 38 39 while(r.size()) r.pop_back(); 40 for(int kk = max(m[i].s - m[i].l,0);kk <= m[i].s - 1;kk ++) 41 { 42 int tmp = dp[i - 1][kk] - kk * m[i].p; 43 44 while(r.size() && r.back().x < tmp) r.pop_back(); 45 str1.k=kk; 46 str1.x=tmp; 47 r.push_back(str1); 48 } 49 for(int j = m[i].s;j <= m[i].s + m[i].l - 1;j ++) 50 { 51 while(r.size() && r.front().k < j - m[i].l) r.pop_front(); 52 dp[i][j] = max(dp[i][j],r.front().x + j * m[i].p); 53 } 54 } 55 int ans = 0; 56 for(int i = 1;i <= k;i ++) 57 ans = max(ans,dp[n][i]); 58 printf("%d\n",ans); 59 return 0; 60 }