CF8C Looking for Order(状压dp)

Description

平面坐标系中有 n n n 个物品 ( x 1 ∼ n , y 1 ∼ n ) (x_{1\sim n},y_{1\sim n}) (x1n,y1n),从 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 走到 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 所需的时间为 ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 (x_1 - x_2)^2 + (y_1 - y_2)^2 (x1x2)2+(y1y2)2。起点在 ( s x , s y ) (sx,sy) (sx,sy),要把所有的物品带到起点,最多可以捡起两个物品。不允许中途放下,求所需的最短时间。

1 ≤ n ≤ 24 1 \leq n \leq 24 1n24

Solution

捡物品可以分成若干个过程,每个过程包括,从起点出发,捡起一个或两个物品,回到起点。每个过程是相互独立的,所以顺序是随意的。

先考虑状压 dp,预处理两点间所需时间(包括起点) d i s dis dis。令 f m a s k f_{mask} fmask m a s k mask mask 状态下所需最短时间。枚举 m a s k mask mask i i i j j j。其中 i i i j j j 为选哪两个物品拿,如果 i = j i = j i=j 相当于只拿 i i i,如果 m a s k mask mask 中不包括 i i i j j j,那可以转移。

f = ∞ f 0 = 0 f = \infty \quad f_0 = 0 f=f0=0

f m a s k ∣ ( 1 <  ⁣ < ( i − 1 ) ) ∣ ( 1 <  ⁣ < ( j − 1 ) ) = f m a s k + d i s 0 , i + d i s i , j + d i s j , 0 f_{mask | (1 <\!< (i - 1)) | (1 <\!< (j - 1))} = f_{mask} + dis_{0,i} + dis_{i,j} + dis_{j,0} fmask(1<<(i1))(1<<(j1))=fmask+dis0,i+disi,j+disj,0

a n s = f ( 1 <  ⁣ < n ) − 1 ans = f_{(1 <\!< n) - 1} ans=f(1<<n)1

然后会收获 TLE \text{TLE} TLE 的好成绩,因为时间复杂度为 O ( n 2 × 2 n ) O(n^2 \times 2^n) O(n2×2n)。因为有许多状态是重复的,举个栗子

110101 = 000000   ∣ { 110000   ∣   000101 000101   ∣   110000 100100   ∣   010001 010001   ∣   100100 ⋯ 110101 = 000000\ | \begin{cases} 110000 \ | \ 000101 \\ 000101 \ | \ 110000 \\ 100100 \ | \ 010001 \\ 010001 \ | \ 100100 \\ \cdots \end{cases} 110101=000000 110000  000101000101  110000100100  010001010001  100100

而之前提到了顺序是随意的,为了避免重复枚举,不如给它定一个顺序。所以在第一个合法的 i i i 跳出循环,枚举下一个状态。

题目还要求存储状态,所以我们记录每个状态是从哪里转移来的即可。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5, M = (1 << 24) + 5;
int x[N], y[N], dis[N][N], f[M], n, pre[M];
int main() {
	scanf("%d%d%d", &x[0], &y[0], &n);
	for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);	
	for (int i = 0; i <= n; i++)
		for (int j = i + 1; j <= n; j++) 
			dis[i][j] = dis[j][i] = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
	memset(f, 0x3f, sizeof(f)); f[0] = 0;
	for (int mask = 0; mask < (1 << n); mask++) {
		for (int i = 1; i <= n; i++) {
			if (mask & (1 << (i - 1))) continue;
			for (int j = 1; j <= n; j++) {
				if (mask & (1 << (j - 1))) continue;
				int nxt = mask | (1 << (i - 1)) | (1 << (j - 1)) ;
				if (f[nxt] <= f[mask] + dis[0][i] + dis[i][j] + dis[j][0]) continue;
				f[nxt] = f[mask] + dis[0][i] + dis[i][j] + dis[j][0];
				pre[nxt] = mask;
			}
			break;
		} 
	} 
	int now = (1 << n) - 1; 
	printf("%d\n", f[now]);
	while (now) {
		printf("0 ");
		for (int i = 1; i <= n; i++)
            if ((now ^ pre[now]) & (1 << (i - 1)))
                printf("%d ", i);
        now = pre[now];
	}
	puts("0");
	return 0;
}
posted @ 2020-02-20 11:01  ylxmf2005  阅读(51)  评论(0)    收藏  举报