ivision (DP_斜率优化|四边形不等式优化)
2013-08-14 21:35
309 查看
Hdu 3480 Division (DP_斜率优化|四边形不等式优化)
分类: 全部博客 ACM_动态规划(DP)2012-08-2515:58 507人阅读 评论(3) 收藏 举报
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480
题目大意:给定一个大小为n的集合,要求将集合分成m个子集合,每个集合都有权值,权值为最大值减最小值的平方。m<=5000,n<=10000.
解题思路:还算简单的优化DP吧,因为一眼就能看出是DP而且需要优化。
要将集合分成m个子集合,并且权值和最小,容易想到对集合元素进行排序,能排序的前提是排序完再划分集合,那么此时划分的集合权值总和将和没排序时的相比相等或者更小,并且使得计算更加容易。比如a<b<c<d,那么(d-a)^2 + (c-b)^2 > (b-a)^2 + (d-c)^2.
对于排序完的集合, 状态转移方程为:dp[i][j+1] = Min {dp[k][j] + (arr[i] - arr[k+1])^2}(k < i),朴素的做法复杂度是O(n*m*n),太可怕了。然后呢,就想到斜率优化或者四边形不等式优化了。
这题既能用斜率优化也能用四边形不等式优化,而斜率优化比四边形不等式优化常数更小,速度更快,本文章着重讲斜率优化,据lasten大神说斜率优化能过的题目四边形不等式都能过,不知可信不可信。
dp[i][j+1] = min{dp[k][j] + (arr[i] - arr[k+1]) ^2} = dp[k][j] + arr[i]^2 + arr[k+1]^2 - 2 * arr[i] * arr[k+1].
设y = dp[k][j] + arr[k+1]^2,x = arr[k+1],,那么dp[i][j] = y - 2 * arr[i] * x,转化成这样就可以开始套模版了。
这题我写了很多遍,一拿到题目用四边形就ac了。然后开始用斜率优化写,之前都是写dp[i][j+1] = dp[k][j] + sum[i] + sum[k] - 2 * sum[i][k],这种和只k有关的DP,用别人的模版倒挺顺的,现在换成了和K+1有关就不知道该怎么搞了。晕了好久,直到今天做网络热身赛遇到差不多的题目才想清楚。其实本质是一样的,写法也是一样,但要先处理j==1的的情况。因为这种时候dp[i][1] = (arr[i] - arr[1])^2都是从0转移过来的,可以不跑单调队列。
测试数据:
Input:
10
3 1
1 2 3
3 2
1 2 2
3 2
1 2 4
4 2
4 7 10 1
OutPut:
Case 1: 4
Case 2: 0
Case 3: 1
Case 4: 18
C艹代码:
[cpp] view
plaincopy
//斜率优化DP
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MIN 5010
#define MAX 10010
#define INF 2147483647;
struct point{
int x,y;
}pot[MAX];
int dp[MAX][MIN];
int qu[MAX],head,tail;
int n, m, arr[MAX];
int CheckIt(int x,int y,int z) {
point p0 = pot[x],p1 = pot[y],p2 = pot[z];
return (p0.x - p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x) <= 0;
}
int NotBest(int x,int y,int k) {
point p0 = pot[x],p1 = pot[y];
return p0.y - p0.x * k > p1.y - p1.x * k;
}
int Solve_DP() {
int i, j, k, mmin;
for (i = 1; i <= n; ++i)
dp[i][1] = (arr[i] - arr[1]) * (arr[i] - arr[1]);
for (j = 2; j <= m; ++j) {
head = tail = 0;
for (i = j; i <= n; ++i) {
pot[i].x = arr[i];
pot[i].y = dp[i-1][j-1] + arr[i] * arr[i];
while (head <= tail - 1 &&
CheckIt(qu[tail-1],qu[tail],i)) tail--;
qu[++tail] = i;
while (head + 1 <= tail &&
NotBest(qu[head],qu[head+1],2*arr[i])) head++;
k = qu[head];
dp[i][j] = pot[k].y - 2 * pot[k].x * arr[i] + arr[i] * arr[i];
}
}
return dp
[m];
}
int main()
{
int i, j, k, t, cas = 0;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (i = 1; i <= n; ++i)
scanf("%d", &arr[i]);
sort(arr + 1, arr + 1 + n);
int ans = Solve_DP();
printf("Case %d: %d\n", ++cas, ans);
}
}
[cpp] view
plaincopy
//四边形不等式优化
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 5010
#define INF 2147483647;
int dp[MAX * 2][MAX];
int n, m, arr[MAX * 2], s[MAX * 2][MAX];
int Solve() {
int i, j, k, mmin;
for (j = 1; j <= m; ++j) s[n + 1][j] = n;
for (i = 1; i <= n; ++i)
dp[i][1] = (arr[i] - arr[1]) * (arr[i] - arr[1]),s[i][1] = 0;
for (j = 2; j <= m; ++j)
for (i = n; i >= 1; --i) {
int mmin = INF;
for (k = s[i][j - 1]; k <= s[i + 1][j] && k < i; ++k)
if (dp[k][j - 1] + (arr[i] - arr[k + 1]) * (arr[i] - arr[k + 1]) < mmin)
mmin = dp[k][j - 1] + (arr[i] - arr[k + 1]) * (arr[i] - arr[k + 1]), s[i][j] = k;
dp[i][j] = mmin;
}
return dp
[m];
}
int main() {
int i, j, k, t, cas = 0;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (i = 1; i <= n; ++i)
scanf("%d", &arr[i]);
sort(arr + 1, arr + 1 + n);
int ans = Solve();
printf("Case %d: %d\n", ++cas, ans);
}
}
Hdu 2829 Lawrence (DP_四边形优化|斜率优化)
分类: 全部博客 ACM_好题经典题 ACM_动态规划(DP)2012-08-2316:42 430人阅读 评论(0) 收藏 举报
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2829
题目大意:给定一个长度为n的序列,至多将序列分成m段,每段序列都有权值,权值为序列内两个数两两相乘之和。m<=n<=1000.
解题思路:经典的DP优化题,可以用四边形不等式优化也可以用斜率优化,我三种方法实现,两种斜率优化,一种四边形不等式,其中复杂度都为n*m,但是常熟略有差异。
状态转移方程很好想,dp[i][j] = min(dp[i][j],dp[k][j-1]+cost[k+1][j])(1<=k<i),这种方程普通写法是n*n*m,当n为1000时运算量为10亿级别,必须优化。
第一种:四边形不等式优化,这种方法是最简单的,主要是减少枚举k的次数。cost[i][j]是某段区间的权值,当区间变大,权值也随之变大,区间变小,权值也随之变小,此时就可以用四边形不等式优化。
我们设s[i][j]为dp[i][j]的前导状态,即dp[i][j] = dp[s[i][j][j-1] + cost[s[i][j]+1][j].之后我们枚举k的时候只要枚举s[i][j-1]<=k<=s[i+1][j],此时j必须从小到大遍历,i必须从大到小。
用这种方法我的代码跑了140ms。
第二种:斜率优化.其实是借鉴大牛大思路,Here,我只是抛砖引玉而已。这种方法的dp和suma数组必须为64位整数,因为平方和会超过32位整数。
用这种方法我的代码跑了350ms。
第三种:斜率优化.其实是借鉴大牛大思路,Here,我只是抛砖引玉而已。其实这题可以作为模板题,斜率优化大抵如此吧。
用这种方法我的代码跑了109ms。
测试数据:
Input:
4 1
4 5 1 2
4 2
4 5 1 2
5 3
1 2 1 2 1
6 4
7 5 3 6 8 9
10 3
1 4 2 7 5 6 8 5 6 9
OutPut:
17
2
92
15
187
C艹代码:
[cpp] view
plaincopy
//四边形不等式
#include <stdio.h>
#include <string.h>
#define MAX 1100
#define INF (1<<30)
int n,m,sum[MAX],cost[MAX][MAX];
int arr[MAX],dp[MAX][MAX],s[MAX][MAX];
void Initial() {
int i, j, k;
for (i = 1; i <= n; ++i)
for (j = 1; j <= n; ++j)
if (j < i) cost[i][j] = 0;
else cost[i][j] = cost[i][j - 1] + arr[j] * (sum[j - 1] - sum[i - 1]);
for (i = 0; i <= n; ++i) {
dp[i][0] = cost[1][i];
s[i][0] = 0,s[n+1][i] = n;
}
}
int Solve_DP() {
int i,j,k;
for (j = 1; j <= m; ++j)
for (i = n; i >= 1; --i) {
dp[i][j] = INF;
for (k = s[i][j-1] ; k <= s[i+1][j]; ++k)
if (dp[k][j-1] + cost[k+1][i] < dp[i][j]) {
s[i][j] = k;
dp[i][j] = dp[k][j-1] + cost[k+1][i];
}
}
return dp
[m];
}
int main()
{
int i,j,k;
while (scanf("%d%d",&n,&m),n+m) {
for (i = 1; i <= n; ++i)
scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1];
Initial();
int ans = Solve_DP();
printf("%I64d\n",ans);
}
}
[cpp] view
plaincopy
//sum[i] = arr[1] + .. arr[i]^2
//sum2[i] = arr[1]^2 + .. arr[i]^2;
//dp[i][j] = min{dp[k][j-1] -sum[i] * sum[k] + (suma[k] - sum[k]^2)/2 + (sum[k]^2 - suma[k])/2};
//斜率优化二
#include <stdio.h>
#include <string.h>
#define MAX 1100
#define INF (1<<30)
#define int64 __int64//long long
struct point {
int64 x,y;
}pot[MAX];
int head,tail,qu[MAX];
int n,m,arr[MAX];
int64 sum[MAX],sum2[MAX],dp[MAX][MAX];
void Initial() {
for (int i = 1; i <= n; ++i) {
sum[i] = arr[i] + sum[i-1];
sum2[i] = arr[i] * arr[i] + sum2[i-1];
dp[i][0] = dp[i-1][0] + arr[i] * sum[i-1];
}
}
int CheckIt(point p0,point p1,point p2) {
return (p0.x-p1.x) * (p0.y-p2.y) - (p0.y-p1.y) * (p0.x-p2.x) <= 0;
}
int NotBest(point p0,point p1,int k) {
return p0.y - k * p0.x > p1.y - k * p1.x;
}
int Solve_DP() {
int i,j,k;
for (j = 1; j <= m; ++j) {
head = 0,tail = 0;
qu[tail] = 0;
for (i = j + 1; i <= n; ++i) {
pot[i].x = sum[i-1];
pot[i].y = dp[i-1][j-1] + (sum[i-1] * sum[i-1] + sum2[i-1]) / 2;
while (head <= tail - 1 &&
CheckIt(pot[qu[tail-1]],pot[qu[tail]],pot[i])) tail--;
qu[++tail] = i;
while (head + 1 <= tail &&
NotBest(pot[qu[head]],pot[qu[head+1]],sum[i])) head++;
k = qu[head];
//dp[i][j] = y - k * x + c
dp[i][j] = pot[k].y - sum[i] * pot[k].x + (sum[i] * sum[i] - sum2[i]) / 2;
}
}
return dp
[m];
}
int GetInt() {
char ch = ' ';
while (ch < '0' || ch > '9')
ch = getchar();
int x = 0;
while (ch >= '0' && ch <= '9')
x = x * 10 + ch - '0',ch = getchar();
return x;
}
int main()
{
int i,j,k;
while (scanf("%d%d",&n,&m),n+m) {
for (i = 1; i <= n; ++i)
scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1];
Initial();
int ans = Solve_DP();
printf("%d\n",ans);
}
}
[cpp] view
plaincopy
//cost[k+1][i]=cost[1][i]-cost[1][k]-sum[k]*(sum[i]-sum[k])
//dp[i][j]=dp[k][j-1]+cost[1][i]-cost[1][k]-sum[k]*(sum[i]-sum[k])
// =dp[k][j-1]-cost[1][k]+sum[k]^2-sum[i]*sum[k]+cost[1][i]
//斜率优化一
#include <stdio.h>
#include <string.h>
#define MAX 1100
#define INF (1<<30)
struct point {
int x,y;
}pot[MAX];
int head,tail,qu[MAX];
int n,m,arr[MAX],cost[MAX];
int sum[MAX],sum2[MAX],dp[MAX][MAX];
void Initial() {
for (int i = 1; i <= n; ++i) {
sum[i] = arr[i] + sum[i-1];
cost[i] = cost[i-1] + arr[i] * sum[i-1];
dp[i][0] = cost[i];
}
}
int CheckIt(point p0,point p1,point p2) {
return (p0.x-p1.x) * (p0.y-p2.y) - (p0.y-p1.y) * (p0.x-p2.x) <= 0;
}
int NotBest(point p0,point p1,int k) {
return p0.y - k * p0.x > p1.y - k * p1.x;
}
int Solve_DP() {
int i,j,k;
for (j = 1; j <= m; ++j) {
head = 0,tail = 0;
qu[tail] = 0;
for (i = j + 1; i <= n; ++i) {
pot[i].x = sum[i-1];
pot[i].y = dp[i-1][j-1] - cost[i-1] + sum[i-1] * sum[i-1];
while (head <= tail - 1 &&
CheckIt(pot[qu[tail-1]],pot[qu[tail]],pot[i])) tail--;
qu[++tail] = i;
while (head + 1 <= tail &&
NotBest(pot[qu[head]],pot[qu[head+1]],sum[i])) head++;
k = qu[head];
//dp[i][j] = y - k * x + c
dp[i][j] = pot[k].y - sum[i] * pot[k].x + cost[i];
}
}
return dp
[m];
}
int main()
{
int i,j,k;
while (scanf("%d%d",&n,&m),n+m) {
for (i = 1; i <= n; ++i)
scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1];
Initial();
int ans = Solve_DP();
printf("%d\n",ans);
}
}
相关文章推荐
- HDU-2829 Lawrence 【斜率优化DP】【四边形不等式优化】
- hdoj 2829 Lawrence 四边形不等式优化dp
- [bzoj1597][USACO2008]土地购买(DP斜率优化/四边形优化)
- hdoj 2829 Lawrence 四边形不等式优化dp
- [HDU2829] Lawrence [四边形不等式优化dp]
- [dp专题-四边形不等式优化]51nod 1022
- Hdu 2829 Lawrence(dp+四边形优化或斜率优化)
- 省选算法学习-dp优化-四边形不等式
- Lawrence - HDU 2829斜率优化,四边形不等式优化
- [转]四边形不等式优化dp(POJ1160)
- HDU-2829 Lawrence 四边形优化DP 斜率优化
- HDU 2829 Lawrence(四边形优化dp/斜率优化dp)
- BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】
- dp专辑 U - Lawrence [ 四边形不等式优化]
- [dp专题-四边形不等式优化]51nod 1022
- dp优化专辑 T - Post Office [ 四边形不等式优化]
- 51nod oj 1022 石子归并 V2 【环形区间DP----四边形不等式优化】
- hdu 2829 Lawrence (dp斜率优化||四边形优化)
- POJ 1160 Post Office 四边形不等式优化DP
- DP 最优二叉树 的四边形不等式优化