「BZOJ4207」「FJ2015集训」Can’t Stop
「题目描述」
这个问题是源于一个在棋盘上玩的,由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 ≤ 105.
对于最多6个case, 1 ≤ N ≤ 105.
对于其他所有的case, 1 ≤ N ≤ 103.
题解
枚举起点暴力45分,加点优化可以跑到60分
考虑分治,分成俩半,考虑跨越中间的序列,中间位置枚举选了哪个数,然后向两边几种情况拓展一下
递归两边区间
复杂度\(O(Tnlogn*d^k*2^(k-1))\)
加点剪枝就能秒了
暴力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include<map> #include<set> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<iostream> #include<algorithm> #define ll long long #define inf 1000000000 using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int T,n,d,K; int a[400005][5]; int ansl,ansr,mx,ans[5]; void dfs(int x,int k) { mx=max(mx,x); if(x==n+1) { return; } for(int i=1;i<k;i++) for(int j=1;j<=d;j++) if(a[x][j]==ans[i]) { dfs(x+1,k); return; } if(k==K+1)return; for(int i=1;i<=d;i++) { ans[k]=a[x][i]; dfs(x+1,k+1); } } int main() { // freopen("stop.in","r",stdin); // freopen("stop.out","w",stdout); T=read(); for(int cas=1;cas<=T;cas++) { printf("Case #%d: ",cas); ansl=inf;ansr=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(); for(int i=1;i<=n;i++) { mx=0; dfs(i,1); if(mx-i>ansr-ansl)ansr=mx,ansl=i; } printf("%d %d\n",ansl-1,ansr-2); } return 0; } |
ac
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
#include<map> #include<set> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<iostream> #include<algorithm> #define ll long long #define inf 1000000000 using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int T,n,d,K; int a[400005][5]; int ansl,ansr; bool mark[100005]; bool check(int x) { for(int i=1;i<=d;i++)if(mark[a[x][i]])return 1; return 0; } void dfs(int l,int r,int lx,int rx,int k) { while(check(l-1)&&l>lx)l--; while(check(r+1)&&r<rx)r++; if(r-l>ansr-ansl)ansl=l,ansr=r; if(r-l==ansr-ansl&&l<ansl)ansl=l,ansr=r; if(k==K)return; for(int i=1;i<=d;i++) { if(l!=lx) { mark[a[l-1][i]]=1; dfs(l-1,r,lx,rx,k+1); mark[a[l-1][i]]=0; } if(r!=rx) { mark[a[r+1][i]]=1; dfs(l,r+1,lx,rx,k+1); mark[a[r+1][i]]=0; } } } void solve(int l,int r) { if(r-l<ansr-ansl)return; if(l>r)return; int mid=(l+r)>>1; for(int i=1;i<=d;i++) { mark[a[mid][i]]=1; dfs(mid,mid,l,r,1); mark[a[mid][i]]=0; } solve(l,mid-1);solve(mid+1,r); } int main() { // freopen("stop.in","r",stdin); // freopen("stop.out","w",stdout); T=read(); for(int cas=1;cas<=T;cas++) { printf("Case #%d: ",cas); ansl=1;ansr=1; 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("%d %d\n",ansl-1,ansr-1); } return 0; } |
我一直想知道为什么分治算法是logn级别的,感觉除了被最优性剪枝剪掉的,每个点终究还是要被枚举到,求教
不就是分治log层每层搞一遍么