一次优化

来源:互联网 发布:linux ftp 上传文件 编辑:程序博客网 时间:2024/04/28 07:49

问题描述:http://acm.pku.edu.cn/JudgeOnline/showproblem?problem_id=1964

Description
Bob is a strategy game programming specialist. In his new city building game the gaming environment is as follows: a city is built up by areas, in which there are streets, trees,factories and buildings. There is still some space in the area that is unoccupied. The strategic task of his game is to win as much rent money from these free spaces. To win rent money you must erect buildings, that can only be rectangular, as long and wide as you can. Bob is trying to find a way to build the biggest possible building in each area. But he comes across some problems – he is not allowed to destroy already existing buildings, trees, factories and streets in the area he is building in.
Each area has its width and length. The area is divided into a grid of equal square units.The rent paid for each unit on which you're building stands is 3$.
Your task is to help Bob solve this problem. The whole city is divided into K areas. Each one of the areas is rectangular and has a different grid size with its own length M and width N.The existing occupied units are marked with the symbol R. The unoccupied units are marked with the symbol F.

Input
The first line of the input contains an integer K – determining the number of datasets. Next lines contain the area descriptions. One description is defined in the following way: The first line contains two integers-area length M<=1000 and width N<=1000, separated by a blank space. The next M lines contain N symbols that mark the reserved or free grid units,separated by a blank space. The symbols used are:
R – reserved unit
F – free unit
In the end of each area description there is a separating line.

Output
For each data set in the input print on a separate line, on the standard output, the integer that represents the profit obtained by erecting the largest building in the area encoded by the data set.

Sample Input

25 6R F F F F FF F F F F FR R R F F FF F F F F FF F F F F F5 5R R R R RR R R R RR R R R RR R R R RR R R R R

Sample Output

450
问题实质:一个矩阵,元素为0或者1,求所有元素都为1的最大子矩阵
以前在bbs上看到有人问一个与这类似的问题,当时写了一个很笨拙的算法,思路就是简单的强行搜索,
搜索所有可能的子矩阵,并求得最大值,算法实现的时候稍微去掉一些不必要的搜索,时间复杂度大约为
o(n^4)程序如下:
// MaxRectSize.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <ctime>
using namespace std;
/////////////////////////////////////////////////////////////
// global data
const int M = 500;
const int N = 500;
bool b[M][N];
//随机产生一个矩阵
void genMatrix()
{
 srand( (unsigned)time( NULL ) );
 for(int i=0; i<M; i++)
  for(int j=0; j<N; j++)
   b[i][j] = rand()%3;
}
//打印矩阵
void printMatrix()
{
 for(int i=0; i<M; i++)
 {
  for(int j=0; j<N; j++)
  {
   if(b[i][j])
    cout<<"#";
   else
    cout<<" ";
  }
  cout<<endl;
 }
}
//在矩阵b[M][N]中寻找以(x1, y1)为左上角的面积最大的矩阵,并把该最大矩阵的右下角坐标保存为(x2, y2)
//返回该最大面积的矩阵的面积
int max1(int x1, int y1, int& x2, int& y2)
{
 assert( (x1>=0) && (x1<M) && (y1>=0) && (y1<N) );
 assert( b[x1][y1] );
 //cout<<"max1("<<x1<<" "<<y1<<")"<<endl;
 int max_x = x1;
 int max_y = y1;
 while( (max_x<M) && (b[max_x][y1]) ) max_x++; max_x--;
 while( (max_y<N) && (b[x1][max_y]) ) max_y++; max_y--;
 int max_size = max_y - y1 + 1;
 x2 = x1, y2 = max_y;
 for(int i=x1+1; i<=max_x; i++)
  for(int j=y1; j<=max_y; j++)
  {
   if( b[i][j])
   {
    int size = (i - x1 + 1) * (j - y1 + 1);
    if( size > max_size)
    {
     x2 = i;
     y2 = j;
     max_size = size;
    }
   }
   else
   {
    max_y = j - 1;
   }
  }
 return max_size;
}

//在b[M][N]中寻找面积最大的矩形,返回最大矩形的面积
//该矩形左上角坐标为(x1, y1),右下角坐标为(x2, y2)
int max(int& x1, int& y1, int& x2, int& y2)
{
 int max_size = 0;
 for(int i=0; i<M; i++)
 {
  for( int j=0; j<N; j++)
  {
   if( !b[i][j] ) continue;
   int x,y;
   int size = max1(i, j, x, y);
   if( size>max_size )
   {
    max_size = size;
    x1 = i;
    y1 = j;
    x2 = x;
    y2 = y;
   }
   if( (M-i+1)*(N-j+1)<max_size ) break;
  }
  if( (M-i+1)*N<max_size ) break;
 }
 return max_size;
}
int _tmain(int argc, _TCHAR* argv[])
{
 genMatrix();
 //printMatrix();
 int x1,y1,x2,y2,size;
 size = max(x1,y1,x2,y2);
 cout<<"the max rect size is:"<<size<<"[("<<x1<<","<<y1<<"),("<<x2<<","<<y2<<")]"<<endl;
 return 0;
}
上述算法复杂度太高,矩阵大一点就不能应付了,需改进。考虑到把二维压缩到一维来计算。
先考虑此问题的一维的情况,也就是求数组中连续1的最大个数。
根据类似求矩阵中最大子矩阵的算法,得出的算法复杂度为o(n^3).源程序如下:
#include <iostream>
using namespace std;
bool b[1000][1000];
int t[1000];
int LargestSameSequence(int n)
{
 int max = 0;
 int cur = 0;
 for (int i=0; i<n; ++i)
 {
  if (t[i]!=0)
  {
   if(cur>max)
    max = cur;
   cur = 0;
  }
  else
   cur++;
 }
 if(cur>max)
  max = cur;
 return max;
}
void main()
{
 int k,m,n,i,j,h,x,max,cur;
 char c;
 cin>>k;
 for (h=0; h<k; ++h)
 {
  cin>>m>>n;
  memset( &b[0][0], 0, sizeof(b) );
  for (i=0; i<m; ++i)
  {
   for (j=0; j<n; ++j)
   {
    cin>>c;
    while(c!='R' && c!='F')
     cin>>c;
    b[i][j] = (c=='R');
   }
  }
  //
  max = 0;
  for(i=0; i<m; ++i)
  {
   memset(t, 0, sizeof(t));
   if((m-i)*n<max)
    break;
   for(j=i; j<m; ++j)
   {
    for(x=0; x<n; ++x)
     t[x] += b[j][x];
    cur = LargestSameSequence(n) * (j-i+1);
    if(max<cur)
     max = cur;
   }
  }
  cout<<max*3<<endl;
 }
}
比我第一次写的提高了一个数量级,应该可以通过了吧?提交之后发现严重超时,没办法,继续补救。
另一个思路是转换为直方图中的最大矩形问题
(http://acm.pku.edu.cn/JudgeOnline/showproblem?problem_id=2559),
算法复杂度可以降到近似o(n^2),源程序如下:
#include "stdafx.h"
#include <stdio.h>
#include <memory.h>
int h[1002];
int l[1001];
int r[1002];
int MaxRect(int n)
{
 h[0] = -1;
 l[0] = -1;
 int i,j;
 for (i=1; i<=n; ++i)
 {
  j = i-1;
  while(h[i]<=h[j]) j = l[j];
  l[i] = j;
 }
 h[n+1] = -1;
 r[n] = n + 1;
 for (i=n; i>=1; --i)
 {
  j = i+1;
  while(h[i]<=h[j]) j = r[j];
  r[i] = j;
 }
 int size,max = 0;
 for (i=1; i<=n; ++i)
 {
  size = h[i]*(r[i]-l[i]-1);
  if(max<size)
   max = size;
 }
 return max;
}
void main()
{
 int k,m,n,i,j,no,max,cur;
 char c;
 scanf("%d",&k);
 for (no=0; no<k; ++no)
 {
  scanf("%d%d",&m,&n);
  memset( h, 0, sizeof(h) );
  max = 0;
  for (i=0; i<m; ++i)
  {
   for (j=1; j<=n; ++j)
   {
    scanf("%c",&c);
    while(c!='R' && c!='F')
     scanf("%c",&c);
    if (c=='R')
     h[j] = 0;
    else
     ++h[j];
   }
   cur = MaxRect(n);
   if(max<cur)
    max = cur;
  }
  printf("%d/n",max*3);
 }
}
这样基本可以满足要求,效果也可以。不过发现有其他人的算法仍然是我10倍,不过内存我好像花的很少,
还要继续考虑下如何空间换时间。