您的位置:首页 > 其它

决策单调性Ⅰ:四边形不等式(bzoj 1563: [NOI2009]诗人小G)

2017-10-08 14:13 405 查看
题目描述:

给出n个数字和常数L,你可以任意合并相邻的两个数字a[x]和a[x+1],并得出一个新的数a[x]+a[x+1]+1,一通合并后得到一个有若干个数的序列,这个序列的不协调值为∑|(a[i]-L)^p|,求最小不协调值

例如L=7,初始4个数分别为3 3 1 5,那么肯定是将前两个数合并,后两个数合并得出7 7,这时不协调值一定为0

n<=100000,L<=300000,P<=10

设dp[i]为前i个数的最小不协调度,len[]为第i个数有



复杂度O(n²P),肯定过不去

当P=2时,设sum[]为len[]的前缀和,若k点比j点更优,且j<k<i,那么有



维护一个下凸包,这样当P=2时,斜率优化复杂度O(n)

斜率优化可见:决策单调性Ⅱ:斜率优化

这就是题目
1010: [HNOI2008]玩具装箱toy 的题解

可这题P不一定等于2,还是过不去

不过以上说明了这个DP可能是有决策单调性的

那么先说:什么是决策单调性?

先提一个词:1D/1D动态规划,指的是状态为O(n),每一个状态决策都需要O(n)遍历前面所有决策才能得出的动态规划,直接求解的复杂度是O(n²),但很多情况下都可以通过一系列手段优化成O(nlogn)或者O(n)(例如平行四边形优化 和斜率优化)

1D/1D动态规划经典模型:



假设j点是当前i点的最优决策,那么决策单调性就是指:

对于所有的x>i,最优决策p(x)一定都大于j

也可这么理解:假设a是dp[x]的最优决策,b是dp[x-1]点的最优决策,a>b,那么对于所有的p>x,从a点转移一定比b点更优,对于所有的p<x-1,从b点转移一定比a点更优

还可以这么理解:每个决策点能决策的区间一定是连续的一段,并且随着决策点的右移,这个区间也在不断右移

充要条件:



具体证明比较复杂,而且题目不同证明应该也会有不同,这里就略

贴一篇证明https://www.byvoid.com/zhs/blog/noi-2009-poet

一般题目都能看出来,没必要证,等你证完别人都AC了

那么知道了决策单调性,怎么搞?

通过上面蓝色字体,可以这样思考:对于当前决策点,它能影响的区间是哪些?

举个例子假设n=30,一步一步来:

先搞出dp[1],那么所有后续状态dp[x]的目前最优决策一定都是1,决策表如下:

111111111111111111111111111111

然后再算出dp[2],由于决策单调性,决策表一定会变成这样:

111111111122222222222222222

再算出dp[3]:111111111122222233333333333

再算出dp[4]:111111111122222244444444444

再算出dp[5]:111111111122222244555555555

……

那么可以看出,每个决策在决策表中一定会有且只有一个转折点

理论上只要知道这个转折点就可以

具体过程:

用一个栈来维护,里面保存每一个决策的起始位置和终点位置,显然这些位置是首尾相接的

每出现一个新决策,从后往前扫描栈,这个时候有两种情况:

①如果在某个老决策起点处新决策更好,那么把这个老决策直接弹出栈(扔掉),用新决策代替之,就如同上面例子中算出dp[4]之后,决策3被完全抛弃

②如果在某个老决策起点处还是老决策更好,那么转折点必定在这个老决策区间内,二分查找并插入新决策

复杂度分析:每个决策出栈进栈1次,每次二分,所以O(nlogn)

回到这题,看DP式子



这不就是


的模型题么,其中w[i, j]就是后面那一长串

就用上述方法就ok了

1563: [NOI2009]诗人小G

Time Limit: 100 Sec  Memory Limit: 64 MB
Submit: 2736  Solved: 885

[Submit][Status][Discuss]

Description



Input



Output

对于每组数据,若最小的不协调度不超过1018,则第一行一个数表示不协调度若最小的不协调度超过1018,则输出"Too hard to arrange"(不包含引号)。每个输出后面加"--------------------"

Sample Input

4

4 9 3

brysj,

hhrhl.

yqqlm,

gsycl.

4 9 2

brysj,

hhrhl.

yqqlm,

gsycl.

1 1005 6

poet

1 1004 6

poet

Sample Output

108

--------------------

32

--------------------

Too hard to arrange

--------------------

1000000000000000000

--------------------

这题就是上面那个例子,代码里有注释

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
#define Inf 1e18
#define LD long double
int n, L, p, top, st[100005], k[100005], a[100005], sum[100005];
LD dp[100005];
LD Pow(int x, int y)
{
int i;
LD ans = 1;
for(i=1;i<=y;i++)
ans = ans*x;
if(ans<0)
ans = -ans;
return ans;
}
LD Calc(int j, int i)
{
return dp[j]+Pow(sum[i]-sum[j]+i-j-1-L, p);
}
int Find(int x)
{
int l, r, m;
l = 1, r = top;
while(l<r)
{
m = (l+r+1)/2;
if(x>=k[st[m]])
l = m;
else
r = m-1;
}
return r;
}
int Find_pos(int i, int x)
{
int l, r, m;
l = max(i+1, k[x]+1), r = n+1;
while(l<r)
{
m = (l+r)/2;
if(Calc(i, m)<Calc(x, m))
r = m;
else
l = m+1;
}
return r;
}
int main(void)
{
char str[35];
int i, T, x;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &L, &p);
for(i=1;i<=n;i++)
{
scanf("%s", str+1);
a[i] = strlen(str+1);
sum[i] = sum[i-1]+a[i];
}
top = 0, k[0] = 0;
st[++top] = 0;
for(i=1;i<=n;i++)
{
x = st[Find(i)];		//找到i点所在区间是哪个最优决策点的区间
dp[i] = Calc(x, i);		//因为是最优决策点,直接计算
while(k[st[top]]>i && Calc(i, k[st[top]])<Calc(st[top], k[st[top]]))		//对于当前新决策,从后往前扫描栈
top--;				//如果在某个老决策起点处新决策更好,那么直接把这个老决策直接弹出栈(扔掉),用新决策代替之
x = Find_pos(i, st[top]);		//如果在某个老决策起点处还是老决策更好,那么转折点必定在这个老决策区间内,二分查找并插入新决策
if(x==n+1)												//↑↑这里偷个懒,直接扫老决策起点往后的整个数组而不是扫老决策区间
continue;
k[i] = x;
st[++top] = i;		//插入新决策
}
/*for(i=1;i<=n;i++)
{
dp[i] = 7*Inf;
for(j=0;j<=i-1;j++)
dp[i] = min(dp[i], Calc(j, i));
}*/
if(dp
>Inf)
printf("Too hard to arrange\n--------------------\n");
else
printf("%lld\n--------------------\n", (LL)dp
);
}
return 0;
}
/*
1
4 9 2
brysj,
hhrhl.
yqqlm,
gsycl.
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: