poj 1821 Fence
题目大意有
一段长 n n n的篱笆 ( 1 ≤ N ≤ 16000 ) (1 \leq N \leq 16 000) (1≤N≤16000)需要 m m m个人来刷 ( 1 ≤ K ≤ 100 ) (1 \leq K \leq 100) (1≤K≤100)每个工人有 3 3 3个属性 l , p , s l,p,s l,p,s分别表示工人最多刷连续的 l l l段篱笆,每刷一段可以得到 p p p的回报,工人最初的位置在 s s s工人必须刷连续的篱笆,并且要么不刷,刷就必须把自己面前的一段篱笆刷掉(面前的这一段也计入总费用)现在请你安排这 m m m个工人,使他们的收益和最大
读入格式
第一行两个正整数 n , k n,k n,k接下来的 k k k行,每行 3 3 3个正整数 l , p , s l,p,s l,p,s
输出格式
输出一行正整数表示工人们的最大收益。
思路
设计
d
p
dp
dp的状态
d
p
[
i
]
[
j
]
表
示
前
i
个
人
刷
到
了
第
j
个
栅
栏
的
最
大
代
价
d p[i][j]表示前i个人刷到了第j个栅栏的最大代价
dp[i][j]表示前i个人刷到了第j个栅栏的最大代价
转移:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]可以从
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
k
−
1
]
+
(
j
−
k
+
1
)
∗
p
i
dp[i-1][j],dp[i][j-1],dp[i-1][k-1]+(j-k+1)*p_i
dp[i−1][j],dp[i][j−1],dp[i−1][k−1]+(j−k+1)∗pi转移
d
p
[
i
−
1
]
[
j
]
dp[i-1][j]
dp[i−1][j]表示第j栅栏不刷
d
p
[
i
]
[
j
−
1
]
dp[i][j-1]
dp[i][j−1]表示第
i
i
i个人不刷
j
j
j
d
p
[
i
−
1
]
[
k
]
+
(
j
−
k
+
1
)
dp[i-1][k]+(j-k+1)
dp[i−1][k]+(j−k+1)第
i
i
i个人从
k
k
k开始一直刷到
j
j
j(其中
k
k
k需要枚举)
这个样子就得到正确答案了
但是考虑到第
3
3
3种转移最差可能使
O
(
N
)
O(N)
O(N)的
这样总体复杂度可能会达到
O
(
m
∗
n
2
)
O(m*n^2)
O(m∗n2)
所以我们需要将
d
p
dp
dp优化
先放上未经优化的 d p dp dp代码
#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;
}
优化
我们再次看一下转移:
d
p
[
i
−
1
]
[
j
]
dp[i-1][j]
dp[i−1][j]表示第j栅栏不刷
d
p
[
i
]
[
j
−
1
]
dp[i][j-1]
dp[i][j−1]表示第
i
i
i个人不刷
j
j
j
d
p
[
i
−
1
]
[
k
]
+
(
j
−
k
+
1
)
dp[i-1][k]+(j-k+1)
dp[i−1][k]+(j−k+1)第
i
i
i个人从
k
k
k开始一直刷到
j
j
j(其中
k
k
k需要枚举)
这样只有第
3
3
3种转移方式可以优化
我们把第
3
3
3种变一下型可以得到
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
−
1
]
[
k
−
1
]
−
k
∗
p
i
}
+
(
j
+
1
)
∗
p
i
dp[i][j]=max \{ {dp[i-1][k-1]-k*p_i} \}+(j+1)*p_i
dp[i][j]=max{dp[i−1][k−1]−k∗pi}+(j+1)∗pi
这个样子就可以进行单调队列的优化了
将单调队列优化分成
3
3
3段
1
、
1、
1、
1
1
1 到
s
i
−
1
s_i-1
si−1只进入单调队列不转移
2
、
2、
2、
s
i
s_i
si既进入单调队列也转移
3
、
3、
3、
s
i
+
1
s_i+1
si+1到
n
n
n只转移不进入单调队列
注意:要将不符合条件的状态及时清出单调队列,只有当队列不为空的时候才能转移
下面附上单调队列优化过后的 A C AC AC代码
#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;
}