卧薪尝胆,厚积薄发。
二分的代价
Date: Sat Dec 29 16:19:10 CST 2018 In Category: NoCategory

Description:

有一个升序数组和一个没有在这个数组中出现过的数,给出和每个元素比较大小的花费,确定一种方案使得无论这个数是多少,最大花费最小。
$1\leqslant n\leqslant 10^5,1\leqslant cost_i\leqslant 9$

Solution:

首先有一个简单的 $O(n^3)$ 的区间 $DP$ ,就是 $f[i][j]=\min(\max(f[i][k-1],f[k+1][j])+a[k])$ ,但是这个不是很好优化,发现答案一定不会超过 $9\log n$ ,那么就可以利用那个把要求解的答案放到状态里的做法,设 $f[c][x]$ 表示花费 $c$ 的代价左端点为 $x$ 时右端点最远到哪里,那么显然 $f[c][x]$ 关于 $x$ 和 $c$ 都是单调的,那么转移方程为 $f[c][x]=\max(f[c-a_i][i+1](i\leqslant f[c-a_i][x]+1))$ ,但是这样转移还是 $O(n^2)$ 的,于是我们可以枚举 $a_i$ ,那么对于 $a_i$ 相同的由上面的单调性我们一定选最靠后的,那么我们可以预处理 $p[x][i]$ 表示在 $i$ 及之前最靠后的一个 $x$ 的位置,然后按上面那个转移就行了,时间复杂度 $O(n\log n\times 81)$ 。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
int a[MAXN];
int f[180][MAXN];
int p[10][MAXN];
int main()
{
freopen("cost.in","r",stdin);
freopen("cost.out","w",stdout);
char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))
{
a[++n] = c - '0';
c = getchar();
}
for(int i = 1;i <= n;++i)p[a[i]][i] = i;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= 9;++j)p[j][i] = (p[j][i] == 0 ? p[j][i - 1] : i);
for(int i = 1;i <= n;++i)
{
f[a[i]][i] = i;
for(int j = 1;j < a[i];++j)f[j][i] = i - 1;
}
for(int k = 1;k < 180;++k)
{
for(int i = 1;i <= n;++i)
{//cout << k << " " << i << " : " << endl;
for(int j = 1;j <= min(9,k);++j)
{
int pos = p[j][min(n,f[k - j][i] + 1)];//cout << pos << endl;
if(pos == 0)continue;
if(pos == n)f[k][i] = n;
else f[k][i] = max(f[k][i],f[k - j][pos + 1]);
}//cout << k << " " << i << " " << f[k][i] << endl;
}
if(f[k][1] >= n)
{
cout << k << endl;
break;
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
In tag: DP-区间DP
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡