这个题,我用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.如果不穷举,应该还有高招,请路过的大牛指点。
正在思考中……