分治+爆搜
仔细想想,如果给我一个很长的区间,问我这个区间是不是极好的,我好像都不太会。。。那就考虑乱搞吧
如果数据随机的话,一个直观的感觉是合法长度很短。
考虑爆搜,我们需要提高爆搜的速度,肯定是要让它较快找到最长的解。一个有效的方法是从中间向两边搜。一方面,不考虑枚举选哪一个数,单单就直观上看,枚举起点的搜法像n^2的,分治的搜法像nlogn的,所以后者应该会更快。另一方面,如果答案很长则分治从中间开始很容易找到最优解,然后最优性剪枝就可以飞起来。这样递归往两边分治,能够较快找到最长答案。相反,如果答案很短,那每一次爆搜的长度就很短。到底能不能卡我也不清楚,反正它能过。
不小心又港记了
#include<cstdio>
#define N 100005
#define D 5
using namespace std;
namespace runzhe2000
{
int read()
{
int r = 0; char c = getchar();
for(; c < '0' || c > '9'; c = getchar());
for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
return r;
}
int n, d, k, a[N][D], ans, ansl, ansr, liml, limr, vis[N], len;
bool invis(int *x)
{
for(int i = 1; i <= d; i++)
if(vis[x[i]])return true;
return false;
}
void dfs(int l, int r, int f)
{
if(f > k) return;
for(; invis(a[l-1]) && liml < l; l--);
for(; invis(a[r+1]) && r < limr; r++);
if(liml < l)
{
int *al = a[l-1];
for(int i = 1; i <= d; i++)
{
vis[al[i]] = 1;
dfs(l-1,r,f+1);
vis[al[i]] = 0;
}
}
if(r < limr)
{
int *ar = a[r+1];
for(int i = 1; i <= d; i++)
{
vis[ar[i]] = 1;
dfs(l,r+1,f+1);
vis[ar[i]] = 0;
}
}
(len = r-l+1) > ans || (len == ans && l < ansl) ? ans = len, ansl = l, ansr = r : 0;
}
void solve(int l, int r)
{
if(l > r || r-l+1 < ans) return;
int mid = (l+r)>>1, *amid = a[mid];
liml = l; limr = r;
for(int i = 1; i <= d; i++)
{
vis[amid[i]] = 1;
dfs(mid,mid,1);
vis[amid[i]] = 0;
}
solve(l,mid-1); solve(mid+1,r);
}
void main()
{
int T = read();
for(int TT = 1; TT <= T; TT++)
{
ans = 0;
n = read(), d = read(), k = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= d; j++)
a[i][j] = read();
solve(1,n);
printf("Case #%d: %d %d\n",TT,ansl - 1,ansr - 1);
}
}
}
int main()
{
runzhe2000::main();
}