poj 1821 Fence
题目大意有
一段长nnn的篱笆(1≤N≤16000)(1 \leq N \leq 16 000)(1≤N≤16000)需要mmm个人来刷 (1≤K≤100)(1 \leq K \leq 100)(1≤K≤100)每个工人有333个属性l,p,sl,p,sl,p,s分别表示工人最多刷连续的lll段篱笆,每刷一段可以得到ppp的回报,工人最初的位置在sss工人必须刷连续的篱笆,并且要么不刷,刷就必须把自己面前的一段篱笆刷掉(面前的这一段也计入总费用)现在请你安排这mmm个工人,使他们的收益和最大
读入格式
第一行两个正整数n,kn,kn,k接下来的kkk行,每行333个正整数l,p,sl,p,sl,p,s
输出格式
输出一行正整数表示工人们的最大收益。
思路
设计dpdpdp的状态dp[i][j]表示前i个人刷到了第j个栅栏的最大代价d
p[i][j]表示前i个人刷到了第j个栅栏的最大代价dp[i][j]表示前i个人刷到了第j个栅栏的最大代价
转移:
dp[i][j]dp[i][j]dp[i][j]可以从dp[i−1][j],dp[i][j−1],dp[i−1][k−1]+(j−k+1)∗pidp[i-1][j],dp[i][j-1],dp[i-1][k-1]+(j-k+1)*p_idp[i−1][j],dp[i][j−1],dp[i−1][k−1]+(j−k+1)∗pi转移
dp[i−1][j]dp[i-1][j]dp[i−1][j]表示第j栅栏不刷
dp[i][j−1]dp[i][j-1]dp[i][j−1]表示第iii个人不刷jjj
dp[i−1][k]+(j−k+1)dp[i-1][k]+(j-k+1)dp[i−1][k]+(j−k+1)第iii个人从kkk开始一直刷到jjj(其中kkk需要枚举)
这个样子就得到正确答案了
但是考虑到第333种转移最差可能使O(N)O(N)O(N)的
这样总体复杂度可能会达到O(m∗n2)O(m*n^2)O(m∗n2)
所以我们需要将dpdpdp优化
先放上未经优化的dpdpdp代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
int r,s=0,c;
for(;!isdigit(c=getchar());s=c);
for(r=c^48;isdigit(c=getchar());(r*=10)+=c^48);
return s^45?r:-r;
}
const int N=110,L=16100;
int n,m;
struct node
{
int l,p,s;
}a[N];
bool cmp(node a,node b)
{
return a.s<b.s;
}
int dp[N][L];
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
a[i].l=read(),a[i].p=read(),a[i].s=read();
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=max(dp[i][j],dp[i][j-1]);//这块不刷
dp[i][j]=max(dp[i][j],dp[i-1][j]);//这个人不刷
if(j<a[i].s)continue;
for(int k=max(1,a[i].s-a[i].l);k<=a[i].s;k++)
{
if((j-k+1)>a[i].l)continue;
dp[i][j]=max(dp[i][j],dp[i-1][k-1]+(j-k+1)*a[i].p);
}
}
}
printf("%d\n",dp[m][n]);
return 0;
}
优化
我们再次看一下转移:
dp[i−1][j]dp[i-1][j]dp[i−1][j]表示第j栅栏不刷
dp[i][j−1]dp[i][j-1]dp[i][j−1]表示第iii个人不刷jjj
dp[i−1][k]+(j−k+1)dp[i-1][k]+(j-k+1)dp[i−1][k]+(j−k+1)第iii个人从kkk开始一直刷到jjj(其中kkk需要枚举)
这样只有第333种转移方式可以优化
我们把第333种变一下型可以得到dp[i][j]=max{dp[i−1][k−1]−k∗pi}+(j+1)∗pidp[i][j]=max \{ {dp[i-1][k-1]-k*p_i} \}+(j+1)*p_idp[i][j]=max{dp[i−1][k−1]−k∗pi}+(j+1)∗pi
这个样子就可以进行单调队列的优化了
将单调队列优化分成333段
1、1、1、111 到 si−1s_i-1si−1只进入单调队列不转移
2、2、2、sis_isi既进入单调队列也转移
3、3、3、si+1s_i+1si+1到nnn只转移不进入单调队列
注意:要将不符合条件的状态及时清出单调队列,只有当队列不为空的时候才能转移
下面附上单调队列优化过后的ACACAC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
int r,s=0,c;
for(;!isdigit(c=getchar());s=c);
for(r=c^48;isdigit(c=getchar());(r*=10)+=c^48);
return s^45?r:-r;
}
const int N=110,L=16100;
int n,m;
struct node
{
int l,p,s;
}a[N];
bool cmp(node a,node b)
{
return a.s<b.s;
}
int dp[N][L];nt q[L],f,b;
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
a[i].l=read(),a[i].p=read(),a[i].s=read();
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++)
{
f=1,b=0;
for(int j=1;j<a[i].s;j++)
{
dp[i][j]=max(dp[i][j],dp[i][j-1]);//这块不刷
dp[i][j]=max(dp[i][j],dp[i-1][j]);//这个人不刷
while((f<=b)&&(j-q[f]+1>a[i].l))f++;
while((f<=b)&&(dp[i-1][q[b]-1]-q[b]*a[i].p<=dp[i-1][j-1]-j*a[i].p))b--;
q[++b]=j;
}
dp[i][a[i].s]=max(dp[i][a[i].s],dp[i][a[i].s-1]);//这块不刷
dp[i][a[i].s]=max(dp[i][a[i].s],dp[i-1][a[i].s]);//这个人不刷
while((f<=b)&&(a[i].s-q[f]+1>a[i].l))f++;
while((f<=b)&&(dp[i-1][q[b]-1]-q[b]*a[i].p<=dp[i-1][a[i].s-1]-a[i].s*a[i].p))b--;
q[++b]=a[i].s;
dp[i][a[i].s]=max(dp[i][a[i].s],dp[i-1][q[f]-1]-q[f]*a[i].p+(a[i].s+1)*a[i].p);
for(int j=a[i].s+1;j<=n;j++)
{
dp[i][j]=max(dp[i][j],dp[i][j-1]);//这块不刷
dp[i][j]=max(dp[i][j],dp[i-1][j]);//这个人不刷
while((f<=b)&&(j-q[f]+1>a[i].l))f++;
if(f<=b)
dp[i][j]=max(dp[i][j],dp[i-1][q[f]-1]-q[f]*a[i].p+(j+1)*a[i].p);
}
}
printf("%d\n",dp[m][n]);
return 0;
}
本文针对POJ1821 Fence问题,详细解析了一种动态规划(DP)算法及其优化策略,旨在最大化工人刷篱笆的总收益。通过引入单调队列优化,有效减少了状态转移的时间复杂度,提高了算法效率。
596

被折叠的 条评论
为什么被折叠?



