【分类讨论 LIS】luogu_P7796 POLICE

题意

给出一个矩阵与目标矩阵。
有两种操作:
1、移动数字到相邻(左右)空位置,耗费0。
2、拿出数字放到空位置,耗费1。
求出最小耗费完成转换。

对于\(50\%\)的数据,保证每本书在初始和最终状态下都会在同一个书架上。
对于所有数据,\(1\leqslant n,m\leqslant1000\)

思路

对于\(50\%\),考虑每行怎么计算。
将每本书它的目标位置存到数组中。
对于这个数组求\(LIS\),那么答案即为\(sz_i-lis_i\),其中\(sz_i\)表示起始与目标在相同书架上的书的个数。
因为移到空位置不需耗费,对于一段前后顺序在操作前与操作后不变的书,可以通过空位置来调整。其余都要抽出。这个最长一段即\(LIS\)

对于\(50\%\),考虑有书本要移到其他书架上的情况。
如果有书要从书架\(i \to j\),将其连边。
形成若干连通块。

对于一个连通块,若里面无空书架,要用其他的书架来进行中转,且目标状态也无空书架。
为了空出一个空书架,这个连通块里的操作次数加上\(1\)

可以发现这个连通块形成一个欧拉回路,在空出一个位置后,其他书架按照欧拉回路来移动,都会出现空出一个位置的情况。
对于一个有空书架的书架里答案就为上面所说的\(sz_i-lis_i\)

对于一个连通块,若里面一开始存在空书架,不用加上\(1\)即可。

连边用并查集来操作。

贴一个更详细分类讨论的博客

代码

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#include <queue>
#include <cstdio>
#include <cstring>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
std::queue<int> q;
int n, m, ans;
int a[1001][1001], emp[1001], sz[1001], lis[1001], fa[1001];
std::pair<int, int> b[1000001];
int tree[1001];

void add(int p, int val) {
	for (; p <= m; p += p & -p)
		tree[p] = std::max(val, tree[p]);
}

int ask(int p) {
	int res = 0;
	for (; p; p -= p & -p)
		res = std::max(res, tree[p]);
	return res;
}

int find(int x) {
	return fa[x] = fa[x] == x ? x : find(fa[x]);
}

void con(int x, int y) {
	int f1 = find(x), f2 = find(y);
	if (f1 == f2)
		return;
	fa[f2] = f1;
	emp[f1] |= emp[f2];
	sz[f1] += sz[f2];
	lis[f1] += lis[f2];
}

inline int read() {
	int res = 0, f = 1;
	char c = getchar();
	while (c < ‘0‘ || c > ‘9‘) {
		if (c == ‘-‘)
			f = -1;
		c = getchar();
	}
	while (c >= ‘0‘ && c <= ‘9‘)
		res = res * 10 + (c ^ 48), c = getchar();
	return res * f;
}

int main() {
	freopen("librarian.in", "r", stdin);
	freopen("librarian.out", "w", stdout);
	n = read(), m = read();
	int dif = 0, empt = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			a[i][j] = read();
			if (!a[i][j])
				emp[i] = 1, empt = 1;
			else
				sz[i]++;
		}
	for (int i = 1, tmp; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			tmp = read();
			if (tmp)
				b[tmp] = std::make_pair(i, j);
			if (a[i][j] != tmp)
				dif = 1;
		}
	if (!dif)
		return !printf("0");
	if (dif && !empt)
		return !printf("-1");
	for (int i = 1; i <= n; i++) {
		memset(tree, 0, sizeof(tree));
		for (int j = 1; j <= m; j++)
			if (b[a[i][j]].first == i)
				q.push(b[a[i][j]].second);
		while (q.size()) {
			 int x = q.front(), tmp = ask(x);
			 q.pop();
			 lis[i] = std::max(lis[i], tmp + 1);
			 add(x, tmp + 1);
		}
	}
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j] && b[a[i][j]].first != i)
				con(i, b[a[i][j]].first);
	for (int i = 1; i <= n; i++)
		if (find(i) == i && sz[i] != lis[i])
			ans += sz[i] - lis[i] + (1 - emp[i]);
	printf("%d", ans);
}

【分类讨论 LIS】luogu_P7796 POLICE

上一篇:Cocos2d-x移植到WindowsPhone8移植问题-libNetwork库移植问题


下一篇:Cocos2d-x移植到WindowsPhone8移植问题-libNetwork库移植问题