jzoj6321 点 (dp)

题面

数轴上n个点,每个点可以二选一:往左d或者往右d。移动后,你可以使用若干条线段覆盖所有点,一条长度为L的线段的代价为a+bL.求最小代价。

n , x , d 150 n,x,d\le 150

题解

听说是zzt的题,果然脑洞做法。
先全部往右移动,再把d*2,就变成了要不要左移d。

这题的做法是按d的大小讨论。
假如d比较小,考虑暴力状压d位,复杂度是 O ( 2 d n ) O(2^dn)
否则,考虑先枚举 0 , d , 2 d , 3 d . . 0, d, 2d,3d.. 的状态
然后按顺序转移,
1 , d + 1 , 2 d + 1... 1, d+1,2d+1...
2 , d + 2 , 2 d + 2... 2,d+2,2d+2...
状态里要压下先前 m a x ( x i ) / d max(xi)/d 个位置的覆盖情况。转移就是讨论是否与上一行的对应位置接起来。最后处理一下最后一行和第一行可能连起来的情况。时间复杂度 O ( 4 m a x / d n ) O(4^{max/d}n)

有d和max/d,d按 2 m \sqrt {2m} 分界最优。

总结

在暴力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;
	}
}
发布了266 篇原创文章 · 获赞 93 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/100046294