子序列(续集) (Data_Structure)

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

正在思考中……

原文地址:https://www.cnblogs.com/HpuAcmer/p/2276862.html