1. 程式人生 > >BZOJ 1293 [SCOI2009] 生日禮物 題解與分析

BZOJ 1293 [SCOI2009] 生日禮物 題解與分析

1293: [SCOI2009]生日禮物

Time Limit: 10 Sec  Memory Limit:162 MB
Submit: 630  Solved: 326
Description 小西有一條很長的綵帶,綵帶上掛著各式各樣的彩珠。已知彩珠有N個,分為K種。簡單的說,可以將綵帶考慮為x軸,每一個彩珠有一個對應的座標(即位置)。某些座標上可以沒有彩珠,但多個彩珠也可以出現在同一個位置上。小布生日快到了,於是小西打算剪一段綵帶送給小布。為了讓禮物綵帶足夠漂亮,小西希望這一段綵帶中能包含所有種類的彩珠。同時,為了方便,小西希望這段綵帶儘可能短,你能幫助小西計算這個最短的長度麼?綵帶的長度即為綵帶開始位置到結束位置的位置差。

Input

第一行包含兩個整數N, K,分別表示彩珠的總數以及種類數。接下來K行,每行第一個數為Ti,表示第i種彩珠的數目。接下來按升序給出Ti個非負整數,為這Ti個彩珠分別出現的位置。

Output

應包含一行,為最短綵帶長度。

Sample Input

6 3
1 5
2 1 7
3 1 3 8

Sample Output

3

HINT

有多種方案可選,其中比較短的是1~5和5~8。後者長度為3最短。
【資料規模】
對於50%的資料, N≤10000;
對於80%的資料, N≤800000;
對於100%的資料,1≤N≤1000000,1≤K≤60,0≤彩珠位置<2^31。

【分析】:           這道題用貪心的思想。貪心的方法為:對於每種顏色的彩珠,從編號小的地方往後推進,用陣列where[x]記錄x顏色的珠子已經推進到了哪個地方。首先,在所有顏色當前推進位置中,選出位置編號的最大值max1和最小值min1,很容易想到當前符合題意的長度就是max1-min1<如圖一>,這一步是計算。
          然後嘗試將某個滿足下面條件的顏色的where向後推一位:這一顏色的當前推進位置是所有顏色中最小的,且它的後面那一位最小,這一步是轉移<如圖二>。           現在來計算迴圈次數:有N個珠子,K種顏色,轉移需轉移N-K次,計算需計算N-K+1次<多的“1”是在初始位置時需要進行一次計算>,因此迴圈次數為N-K+1。這道題貪心之處就在於每次轉移最小值,這樣保證情況最優且考慮全面。 【時間複雜度分析】:           迴圈次數*(轉移+計算):O((N-K+1)*2*K)=O(N*K) 【評測資訊】:           Time:4172 ms ,Memory:9192 kb
【程式碼】:
/************************************************************** 
    Problem: 1293 
    Language: C++ 
    Result: Accepted 
    Time:4172 ms 
    Memory:9192 kb 
****************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define MAX 61
#define IMAX 2147483647
struct BALL{vector<int> w;};
BALL a[MAX];
int N,K,first[MAX],ans=IMAX,min1=IMAX,max1=0;
int where[MAX];
int num[MAX];
int main()
{
      //freopen("input.in","r",stdin);
	  //freopen("output.out","w",stdout); 
	  scanf("%d%d",&N,&K);
      for(int i=1;i<=K;i++)
      {
            int A;
            scanf("%d",&A);
			num[i]=A;
            for(int j=1;j<=A;j++)
            {
                  int B;
                  scanf("%d",&B);
                  a[i].w.push_back(B);
            }
			where[i]=0;
      }
      
	  for(int i=1;i<=N-K+1;i++)
	  {
	        int use=1;
	        min1=IMAX,max1=0;
	        for(int j=1;j<=K;j++)
			{
			      min1=min(min1,a[j].w[where[j]]);
				  max1=max(max1,a[j].w[where[j]]);
		    }
		    ans=min(ans,max1-min1);
		    
		    min1=IMAX;
			for(int j=1;j<=K;j++)
			{
			      if(!(where[j]==num[j]-1) && (a[j].w[where[j]]<min1 || (a[j].w[where[j]]==min1 && a[j].w[where[j]+1]<a[use].w[where[use]+1])))
				  {
				        use=j;
						min1=a[j].w[where[j]];
				  }
			}
			where[use]++;
	  }
      printf("%d\n",ans);
	  //system("pause");
      return 0;
}