CF79D

传送门

题意:初始有 \(n\)\(0\),给定一个序列 \(a\),每次可以选择一个长度为某个 \(a_i\) 的区间,将其全部取反。再给定一个序列 \(x\),要求最后的状态是只有 \(x\) 中的位置是 \(1\)。问最小步数/判断无解。

范围:\(n\le 10000,|a|=l\le 100,|x|=k\le 10\)

关于区间取反的,考虑差分。根据 \(x\) 求出最终需要的差分序列。(注意要包括 \(n+1\) 位置

然后区间取反就变成两个单点取反。

要求从全 \(0\) 开始,每次可以把两个隔了 \(a_i\) 的位置取反,变成目标。

发现这个过程具有可逆性,所以可以看作从目标状态变成全0。

再看一看,发现一定不会把两个0变成两个1,这样一定不优;一个 0 一个 1 取反可以视作交换。

所以每次操作实际上是选定两个 1,经过若干次交换来调整位置,然后把它们消掉。(注意包括 \(n+1\)

注意到 \(k\le 10\),所以差分序列的 \(1\) 个数 \(\le 20\),考虑状压。

\(dp[S]\) 表示 \(S\) 中的 \(1\) 还没有被消除所需的最小次数。(\(S\) 是一个长度 \(\le 20\) 的二进制数,第 \(i\) 位代表原本差分序列的第 \(i\)\(1\)

初值 \(dp[0]=0,dp[S]=+\infty\)

递推:枚举 \(S\) 中两个还是 \(1\) 的位置 \(i,j\),若经过若干次调整位置,将它们消除的代价是 \(f[i][j]\),则 \(dp[S]\leftarrow \min(dp[S],dp[S-2^i-2^j]+f[i][j])\)

怎么求 \(f[i][j]\)?这可以用 BFS。具体而言,对于每个 \(a_i\),我们把所有 \(pos\)\(pos+a_i\) 连边。(要求 \(pos+a_i\le n+1\)

然后从差分序列中每个 \(1\) 的位置分别作为起点,跑 BFS,就求出了这个 \(1\) 与其他 \(1\) 消除的代价。

#include <bits/stdc++.h>

using namespace std;
const int N = (1 << 20) + 5;
int n, k, l, a[105];
int x[10005];

int rev[10005];
vector<int> v;

int f[25][25];
int dp[N];

vector<int> e[10005];

int dist[10005];
void bfs(int s) {
//	if (s == 2)
//		cout << s << endl;
	queue<int> q;
	memset(dist, 0x3f, sizeof dist);
	dist[s] = 0;
	q.push(s);
	while (!q.empty()) {
		int h = q.front();
		q.pop();
//		if (s == 2)
//			cout << h << endl;
		for (auto i: e[h])
			if (dist[i] == 0x3f3f3f3f) {
				dist[i] = dist[h] + 1;
				q.push(i);
			}
	}
}
void init() { //预处理 
	for (int i = 0; i < v.size(); i++) {
		bfs(v[i]); 
		for (int j = 0; j < v.size(); j++) {
			f[rev[v[i]]][rev[v[j]]] = dist[v[j]];
//				printf("%d=>%d:%d\n", rev[i], rev[v[j]], dist[v[j]]);
		}
	}
}



int main() {
	cin >> n >> k >> l;
	for (int i = 1, tmp; i <= k; i++) {
		cin >> tmp;
		x[tmp] ^= 1;
		x[tmp + 1] ^= 1;
	}
	for (int i = 1; i <= l; i++) {
		cin >> a[i];
		for (int j = 1; j + a[i] <= n + 1; j++) {
			e[j].push_back(j + a[i]);
			e[j + a[i]].push_back(j);
		}
	}
	
	
//	for (int i = 1; i <= n + 1; i++)
//		cout << x[i] << ' ';
//	puts("");
	//要把x数组全部消成0 
	for (int i = 1; i <= n + 1; i++)	
		if (x[i] == 1) {
			rev[i] = v.size();
			v.push_back(i);
		}
	init();
//	cout << f[0][1] << endl; 
	
	memset(dp, 0x3f, sizeof dp);
	dp[0] = 0;
	for (int S = 1; S < (1 << v.size()); S++) {
		for (int i = 0; i < v.size(); i++)
			if (((S >> i) & 1) == 1) {
				for (int j = i + 1; j < v.size(); j++)
					if (((S >> j) & 1) == 1)
						dp[S] = min(dp[S], dp[S - (1 << i) - (1 << j)] + f[i][j]);
			}
	}
	if (dp[(1 << v.size()) - 1] == 0x3f3f3f3f)
		cout << -1 << endl;
	else
		cout << dp[(1 << v.size()) - 1] << endl;
	return 0;
}
//1 2 10000 10001
posted @ 2024-03-02 16:46  FLY_lai  阅读(7)  评论(0编辑  收藏  举报