[BZOJ4207]Can

[BZOJ4207]Can

试题描述

这个问题是源于一个在棋盘上玩的,由Sid Sackson设计的名叫Can't stop的游戏的。这个问题与Can't stop有一定的相似之处,但是不需要玩过Can't stop。
你在玩一个(非常大型的)棋盘游戏。在这个游戏里面,给出了一个长度为N的roll set的序列。每个roll set包括D个die roll,每个die roll是一个正整数。
你需要找到序列中总长度最大的极好的区间。区间即为连续的一段roll set。如果存在k个数使某个区间内的所有roll set都至少包含其中一个,那么,这个区间就被认为是极好的。
例如:d=2,k=3时,roll set如下:
 
Set 0: 10 20
Set 1: 50 60
Set 2: 70 30
Set 3: 40 40
Set 4: 30 30
Set 5: 20 40
从0到2的区间是极好的,因为从0到2中的每个roll set都包含了10,50或70 。从1到5的区间也是极好的,因为1到5的所有roll set都包含50,30或40。它包含了5个roll set,是总长度最大的极好的区间。
你的任务是输出总长度最大的极好的区间的第一个元素的下标和最后一个元素的下标。如果有多个长度一样的,输出第一个元素下标最小的。请注意下标从0开始。

输入

第一行包含一个整数T,表示数据组数。接下来T组数据。
每组数据的第一行是三个正整数N,D,k,描述如上。接下来一行,包含N*D个整数,前D个整数表示第一个roll set ,接下来D个表示第二个roll set,以此类推。

输出

对于每个case,输出一行,"Case #x: y z",x表示case标号(从1开始),y和z是答案区间的第一个和最后一个元素的下标。

输入示例

4
8 1 2
1 2 3 2 4 5 4 6
4 3 2
1 2 3 4 5 6 7 8 9 10 11 12
6 2 3
10 20 50 60 70 30 40 40 30 30 20 40
10 1 3
2 4 3 1 4 5 3 1 1 2

输出示例

Case #1: 1 3
Case #2: 0 1
Case #3: 1 5
Case #4: 1 4

数据规模及约定

对于45%的数据,N<=1000
对于50%的数据,k=2
前两部分数据共计70%
对于100%的数据,2<=k<=3
输入文件在4.8M以内
T=10.
1 ≤ D ≤ 4.
1 ≤ 每个die roll ≤ 10^5.
对于最多6个case, 1 ≤ N ≤ 10^5.
对于其他所有的case, 1 ≤ N ≤ 10^3.

题解

这是一道分治 + 暴搜的好题。

做法是这样的,我们在分治时考虑每次分治跨中点的部分,可以从中点开始向两边扩展,一旦遇到当前选的 k’ 个数不能搞掉的 roll set,就枚举一下选择这个 roll set 中的哪一个 die roll,然后接着向左右扩展。

最优性剪枝能剪掉不少情况。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 100010
int n, D, K, A[maxn][4], num[maxn<<2];

bool has[maxn<<2];
int al, ar;
bool can(int p) {
	for(int i = 0; i < D; i++) if(has[A[p][i]]) return 1;
	return 0;
}
void dfs(int l, int r, int ql, int qr, int k) {
	while(l >= ql && can(l)) l--;
	while(r <= qr && can(r)) r++;
//	printf("dfs: %d %d\n", l, r);
	int len = r - l - 1;
	if(len > ar - al + 1) al = l + 1, ar = r - 1;
	if(len == ar - al + 1 && l + 1 < al) al = l + 1, ar = r - 1;
	if(k == K) return ;
	if(l < ql && r > qr) return ;
	for(int i = 0; i < D; i++) {
		if(l >= ql) has[A[l][i]] = 1, dfs(l - 1, r, ql, qr, k + 1), has[A[l][i]] = 0;
		if(r <= qr) has[A[r][i]] = 1, dfs(l, r + 1, ql, qr, k + 1), has[A[r][i]] = 0;
	}
	return ;
}
void solve(int l, int r) {
	if(ar - al + 1 > r - l + 1 || l > r) return ;
	int mid = l + r >> 1;
//	printf("[%d, %d] %d\n", l, r, mid);
	dfs(mid, mid, l, r, 0);
	solve(l, mid - 1); solve(mid + 1, r);
	return ;
}

int main() {
	int T = read();
	for(int kase = 1; kase <= T; kase++) {
		n = read(); D = read(); K = read();
		int cnt = 0;
		for(int i = 0; i < n; i++)
			for(int j = 0; j < D; j++) num[++cnt] = A[i][j] = read();
		sort(num + 1, num + cnt + 1);
		for(int i = 0; i < n; i++)
			for(int j = 0; j < D; j++) A[i][j] = lower_bound(num + 1, num + cnt + 1, A[i][j]) - num;
		al = ar = 0;
		solve(0, n - 1);
		printf("Case #%d: %d %d\n", kase, al, ar);
	}
	
	return 0;
}

 

posted @ 2017-01-20 18:39  xjr01  阅读(458)  评论(0编辑  收藏  举报