题面
数轴上n个点,每个点可以二选一:往左d或者往右d。移动后,你可以使用若干条线段覆盖所有点,一条长度为L的线段的代价为a+bL.求最小代价。
题解
听说是zzt的题,果然脑洞做法。
先全部往右移动,再把d*2,就变成了要不要左移d。
这题的做法是按d的大小讨论。
假如d比较小,考虑暴力状压d位,复杂度是
否则,考虑先枚举
的状态
然后按顺序转移,
状态里要压下先前
个位置的覆盖情况。转移就是讨论是否与上一行的对应位置接起来。最后处理一下最后一行和第一行可能连起来的情况。时间复杂度
有d和max/d,d按 分界最优。
总结
在暴力dp的基础上,第二个做法同时进行多条线段的构造,凑出了一个以d作分母的复杂度解法,因此可以按照数据规模选择不同的解法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 155;
int n, a[N], ans = 1e9;
int d, e, b, mx;
int f[2][10+(1<<22)], o;
int has[455], ful, pre, inf;
int no[455];
inline void chkmin(int &a,int b) { if (a > b) a = b; }
inline int get(int s,int end,int loc) { return s >> (end - loc) & 1; }
int main() {
freopen("point.in", "r", stdin);
freopen("point.out", "w", stdout);
cin>>n>>d>>e>>b;
d <<= 1;
for(int i = 1; i <= n; i++) {
int z; scanf("%d",&z); has[z + d]=1;
mx = max(mx, z + d);
}
if (d <= sqrt(2 * mx)) {
cerr<<"me1"<<endl;
ful = 1 << d + 1, pre = ful >> 1;
memset(f, 127, sizeof f); inf = f[o][0];
f[0][0] = 0;
for(int i = 0; i < mx; i++) {
memset(f[1-o], 127, (1 + ful) * sizeof f[1-o][0]);
for(int s = 0; s < ful; s++) if(f[o][s] != inf) {
int ns = s << 1;
if (ns >= ful) ns -= ful;
if (has[i + 1] == 0 || (ns & pre) != 0)
chkmin(f[1- o][ns], f[o][s]);
if(s & 1) {
chkmin(f[1 - o][ns | 1], f[o][s] + b);
}
chkmin(f[1 - o][ns | 1], f[o][s] + e);
}
o = 1 - o;
}
for(int s = 0; s < ful; s++) chkmin(ans, f[o][s]);
cout << ans << endl;
} else {
int tt = 0;
int col = 0; for(; col * d <= mx; col ++) no[col * d] = ++tt;
ful = 1 << col, pre = ful >> 1;
for(int is = 0; is < ful; is++) {
int can = 1, cnt = 0;
for (int j = 0; j < col; j++) {
if (is >> j & 1) cnt++;
if (has[j * d]) {
if ((j == 0 ? 0 : get(is, col - 1, j - 1)) + get(is, col - 1, j) == 0) {
can = 0; break;
}
}
}
if (!can) continue;
memset(f[o], 127, sizeof f[o]);
f[o][is] = cnt * e;
int ed = 0, u = col;
for(int i = 1; i < d; i++) {
for(int x = i; x <= mx; x += d) {
no[x] = ++u;
memset(f[1 - o], 127, (5 + ful) * sizeof f[1 - o][0]);
for(int s = 0; s < ful; s++) {
int ns = s << 1;
if (ns >= ful) ns -= ful;
if (has[x] == 0 || (s & 1)) {
chkmin(f[1 - o][ns], f[o][s]);
}
if (s >> (no[x] - 1 - no[x - 1]) & 1) {
chkmin(f[1 - o][ns | 1], f[o][s] + b);
}
chkmin(f[1 - o][ns | 1], f[o][s] + e);
}
o = 1 - o;
ed = x;
}
}
ed /= d;
for(int s = 0; s < ful; s++) {
int v = f[o][s];
if (e > b) {
for(int j = 0; j <= ed && j < col - 1; j++) {
if (get(s, ed, j) + get(is, col - 1, j + 1) == 2) {
v += b - e;
}
}
}
ans = min(ans, v);
}
}
cout << ans << endl;
}
}