一、题目
二、解法
首先考虑如果有一个块长和宽都比另一个块要小,那么这个块就没有存在的意义了,我们可以先排序(长为第一关键字,宽为第二关键字,从小到大),然后从后往前扫,期间维护宽的最大值,如果最大值不比当前宽小,那么这个块没用,我们去除完没用的点之后再排一遍序(方法一样)。
这样就可以愉快的
d
p
dp
dp了,设
d
p
[
i
]
dp[i]
dp[i]为划分到
i
i
i的最小花费,转移如下:
d
p
[
i
]
=
d
p
[
j
]
+
a
[
i
]
×
b
[
j
+
1
]
dp[i]=dp[j]+a[i]\times b[j+1]
dp[i]=dp[j]+a[i]×b[j+1]转移是
O
(
n
2
)
O(n^2)
O(n2)的,我们来推一波式子,设两个转移点
j
,
k
j,k
j,k(
j
<
k
j<k
j<k):
d
p
[
j
]
+
a
[
i
]
×
b
[
j
+
1
]
<
d
p
[
k
]
+
a
[
i
]
×
b
[
k
+
1
]
dp[j]+a[i]\times b[j+1]<dp[k]+a[i]\times b[k+1]
dp[j]+a[i]×b[j+1]<dp[k]+a[i]×b[k+1]
d
p
[
j
]
−
d
p
[
k
]
−
b
[
j
+
1
]
+
b
[
k
+
1
]
<
a
[
i
]
\frac{dp[j]-dp[k]}{-b[j+1]+b[k+1]}<a[i]
−b[j+1]+b[k+1]dp[j]−dp[k]<a[i]显然
a
[
i
]
a[i]
a[i]是单调递增的,
−
b
[
i
]
-b[i]
−b[i]也是单调递增的,队首维护上面的式子,队尾保证斜率单调递增,时间复杂度
O
(
n
)
O(n)
O(n),贴个代码。
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const int M = 50005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,mx,h,t,dp[M],q[M];
struct node
{
int a,b;
node(int A=0,int B=0) : a(A) , b(B) {}
bool operator < (const node &B) const
{
if(a==B.a) return b<B.b;
return a<B.a;
}
}s[M],v[M];
int up(int j,int k)
{
return dp[j]-dp[k];
}
int down(int j,int k)
{
return v[k+1].b-v[j+1].b;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=node(read(),read());
sort(s+1,s+1+n);
for(int i=n;i>=1;i--)
{
if(s[i].b<=mx) continue;
mx=max(mx,s[i].b);
v[++m]=s[i];
}
sort(v+1,v+1+m);
for(int i=1;i<=m;i++)
{
while(h<t && up(q[h+1],q[h])<v[i].a*down(q[h+1],q[h])) h++;
dp[i]=dp[q[h]]+v[i].a*v[q[h]+1].b;
while(h<t && up(q[t],q[t-1])*down(i,q[t])>=up(i,q[t])*down(q[t],q[t-1])) t--;
q[++t]=i;
}
printf("%lld\n",dp[m]);
}
本文介绍了一种通过排序和动态规划解决二维矩形区域划分问题的高效算法,通过去除冗余块并利用单调队列优化转移过程,实现了O(n)的时间复杂度。

177

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



