这个题,我用map做的,后来验证是错误的,算法错了。比如这组数据:
18
1 2 2 3 3 4 4 1 2 3 4 1 2 2 3 3 4 4
我的代码得出6,但是正确答案是4.
我是统计每个数出现了多少次,然后从左往右,碰到相同的次数减1,谁刚好到1了(如果大于1,说明右边会存在相同的数)就停下,得出位置first,再从右往左,类似操作,得出end,从first到end之间必然会包括序列中的所有数。但是满足这个条件的子序列不一定是最短的。比如我的算法,会得出的子序列是后面的1 2 2 3 3 4,但是最短的是中间的1 2 3 4。所以算法需要改进。
改进后的代码(WA):
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> #include<iostream> #include<algorithm> using namespace std; #define SIZE 1000001 #define MOD 1000001 typedef struct node { int id,cnt; struct node * next; } Node; int a[SIZE],b[SIZE]; int num; Node x[SIZE]; Node *search(Node x[],int n,int k); void insert(Node x[],int n,int k); int main() { int T,i,n,first,end,ff,ee,r1,r2,rr2; //freopen("in.txt","r",stdin); //freopen("me.txt","w",stdout); /* 18 1 2 2 3 3 4 4 2 1 4 3 1 2 2 3 3 4 4 18 1 2 2 3 3 4 4 1 2 3 4 1 2 2 3 3 4 4 */ scanf("%d",&T); getchar(); do { num = 0; for(i = 0 ; i < SIZE ; ++i) { x[i].id = -1; x[i].next = NULL; } scanf("%d",&n); for(i = 0 ; i < n ; ++i ) { scanf("%d",&a[i]); b[i]=a[i]; insert(x,SIZE,a[i]); } for(first = 0 ;(search(x,SIZE,a[first])->cnt) > 1 ; ++first) --(search(x,SIZE,a[first])->cnt) ; for(end = n-1 ; (search(x,SIZE,a[end])->cnt) > 1 ; --end) --(search(x,SIZE,a[end])->cnt) ; r1 = end - first + 1; i = 0 ; rr2=1000000; while(i <= end) { if(a[i] == a[first]) { ff = i; ++i; for(;a[i] != a[end];++i); ee = i; if(i>end) break; sort(b+ff,b+ee+1); for(i = ff ; i <= ee ; ++i) b[i] = a[i]; int k = 1; for(i = ff+1 ; i <= ee ; ++i ) { if(b[i] != b[i-1]) ++k; } r2 = ee -ff + 1 ; if(k == num && r2 < rr2) rr2 = r2; ++ee; while(a[ee] == a[end]) ++ee; --ee; i = ee; } else { if(a[i] == a[end]) { ee = i; ++i; for(;a[i] != a[first];++i); ff = i; if(i>end) break; sort(b+ee,b+ff+1); for(i = ee ; i <= ff ; ++i) b[i] = a[i]; int k = 1; for(i = ee+1 ; i <= ff ; ++i ) { if(b[i] != b[i-1]) ++k; } r2 = ff-ee + 1 ; if(k == num && r2 < rr2) rr2 = r2; ++ff; while(a[ff] == a[first]) ++ff; --ff; i = ff; }//if else ++i; if(i>end) break; }//else }//while() printf("%d\n",rr2<r1? rr2:r1); }while(--T); system("pause"); return 0; } Node *search(Node x[],int n,int k) { Node* p; int pos; pos = k % MOD; p = &x[pos]; while(p && p->id != k) p = p->next; return p; } void insert(Node x[],int n,int k) { Node *p,*one; int pos; one = (Node *)malloc(sizeof(Node)); p = search(x,n,k); if(p) { ++(p->cnt); free(one); } else { ++num; pos = k % MOD; one->next = x[pos].next; x[pos].next = one; one->id = k; one->cnt = 1; } }
我测了一下,只有2组数据错了。开头提到的那组数据是对的。
但是这个算法依然是有bug的,因为我总是把子序列的开头和结尾定死了(就是first 和 end 位置的那两个数),显然是不对的。
比如这组数据:
18
1 2 2 3 3 4 4 2 4 1 3 1 2 2 3 3 4 4
我的代码运行结果是6,但是结果应该是4,我得出的子序列是后面的1 2 2 3 3 4 ,但是中间的 2 4 1 3 也满足条件,而且长度最短,为4.它的两端是2 和3,而我的算法规定开头和结尾只能是first 和 end位置上的数。
必须谨记:
1.子序列开头和结尾是不能固定的,所以可以穷举;
2.如果穷举,有两种结果:
(1)超时,蛮力穷举必然超时;
(2)AC,这就要进行巧妙的穷举,就是有些显然不符合的,直接跳过。
3.如果不穷举,应该还有高招,请路过的大牛指点。
正在思考中……