[HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1972  Solved: 1043
[Submit][Status][Discuss]

Description

有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

Input

第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

Output

仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

Sample Input

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

Sample Output

1

HINT

 

问题规模

(1)矩阵中的所有数都不超过1,000,000,000

(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100

 

Source

 
[Submit][Status][Discuss]

【算法简析】

问题的规模相当大,O(abn)的算法都会超时,所以只能考虑O(ablogn)或者O(ab)的算法。

下面介绍O(ab)的算法:利用单调队列。

本题中枚举是必不可少的,而且最坏只能是O(ab)的,所以必须为这个O(ab)的枚举计算一些状态,即以(i,j)为左上角的n×n的正方形区域中的最大值和最小值。把这个二维的问题化简成一维,就是以(i,j)为左边的长度为n的序列的最大值max[i,j]和最小值min[i,j],然后用同样的推算方法可以由这个一维的状态可以推算出二维的状态。现在我们的任务就是在O(b)的时间内计算出一行的max[i]min[i]。这就需要借助单调队列了。

开两个队列,一个维护最大值,一个维护最小值。为了方便叙述,下文只讨论最大队,最小队的维护方式类似。

我们要保证队列中各个元素大小单调递减(注意,不是单调不上升),各个元素的下标单调递增。这样才可以保证队首元素最大,而且更新的时候队首永远是当前最大。因此,需要改造一下队列,让它变成能在两头删除,在队尾插入。

为了保证单调性,每次插入的时候,先判断队尾元素,如果不比待插入元素大就删除,不断删除队尾直到队尾元素大于待插入元素或者队空。删除的时候,判断队首,如果队首元素下标小于当前段左边界就删除,不断删除队首直到队首元素下标大于等于当前段左边界(注意:这时队列肯定不为空),队首元素就是当前段的最优解。

有了单调队列,计算max[i]min[i]就方便了。初始的时候分别把当前行前n-1个元素插入队尾。从第1列开始,每次取队首最优值,从队首删除无效元素,并将当前列+n-1号元素插入队尾。由于每个元素最多入队出队各一次,时间复杂度就是O(b)

用相同的方法可以在O(a)时间内根据max[i]min[i]计算出正方形的最优解,时间复杂度为O(ab)

 

 1 #include <iostream>

 2 #include <cstring>

 3 #include <cstdlib>

 4 #include <cstdio>

 5 #include <algorithm>

 6 #define N 1111

 7 

 8 using namespace std;

 9 

10 long long a,b,n;//ÌâÄ¿ÒªÇó,aΪÐÐ,bΪÁУ¬nΪҪÇóµÄÕý·½Ðα߳¤ 

11 long long map[N][N];//´æͼ 

12 long long q[N][N];//¶ÓÁÐ 

13 long long h[N],t[N];//¶ÓÊ×£¬¶ÓβָÕë 

14 long long s,e,dq[N];

15 long long mx[N][N],ans,mn[N][N];

16 

17 inline void getmax()

18 {

19     for(long long i=1;i<=b;i++) h[i]=1,t[i]=0;//³õʼ»¯£¬Í·Ö¸ÕëΪ1£¬Î²Ö¸ÕëΪ0 

20     

21     for(long long i=1;i<=a;i++)//ö¾ÙÐÐ 

22     {

23         for(long long j=1;j<=b;j++)//ö¾ÙÁÐ 

24         {

25             

26             while(h[j]<=t[j]&&i-q[j][h[j]]+1>n)//Èô¶ÓÁв»Îª¿ÕÇÒ³¬¹ýnµÄÏÞÖÆ 

27                 h[j]++;//ɾ³ý¶ÓÊ×ÔªËØ

28                  

29             while(h[j]<=t[j]&&map[q[j][t[j]]][j]<=map[i][j])//Èô¶ÓÁв»Îª¿ÕÇÒÒª²åÈëµÄÔªËØ´ó

30                     t[j]--;                                    //ÓÚ¶ÓβԪËØ 

31             

32             q[j][++t[j]]=i;//²åÈë´ËÔªËØ 

33         }

34         e=0;//βָÕë 

35         s=1;//Í·Ö¸Õë 

36         for(long long j=1;j<=b;j++)//ö¾ÙÁÐ 

37         {

38             while(s<=e&&j-dq[s]+1>n)

39                 s++;

40             while(s<=e&&map[q[dq[e]][h[dq[e]]]][dq[e]]<=map[q[j][h[j]]][j])

41                 e--;

42             dq[++e]=j;

43             mx[i][j]=map[q[dq[s]][h[dq[s]]]][dq[s]];

44         }

45     }

46 }

47 

48 inline void getmin()

49 {

50     for(long long i=1;i<=b;i++) h[i]=1,t[i]=0;

51     for(long long i=1;i<=a;i++)

52     {

53         for(long long j=1;j<=b;j++)

54         {

55             while(h[j]<=t[j]&&i-q[j][h[j]]+1>n) 

56                 h[j]++;

57             while(h[j]<=t[j]&&map[q[j][t[j]]][j]>=map[i][j]) 

58                 t[j]--;

59             q[j][++t[j]]=i;    

60         }

61         e=0; s=1;

62         for(long long j=1;j<=b;j++)

63         {

64             while(s<=e&&j-dq[s]+1>n) 

65                 s++;

66             while(s<=e&&map[q[dq[e]][h[dq[e]]]][dq[e]]>=map[q[j][h[j]]][j]) 

67                 e--;

68             dq[++e]=j;

69             mn[i][j]=map[q[dq[s]][h[dq[s]]]][dq[s]];

70         }

71     }

72 }

73 

74 int main()

75 {

76      scanf("%lld%d%d",&a,&b,&n);

77      for(long long i=1;i<=a;i++)

78         for(long long j=1;j<=b;j++)

79             scanf("%lld",&map[i][j]);

80  

81  

82         getmin();

83      getmax();

84      ans=1LL<<60;

85      for(long long i=n;i<=a;i++)

86         for(long long j=n;j<=b;j++)

87             ans=min(ans,mx[i][j]-mn[i][j]);

88     printf("%lld\n",ans);

89         return 0;

90 }

 

 

 

你可能感兴趣的:(2007)