Google Code Jam 2009预选赛第二题----Watersheds

Problem

Geologists sometimes divide an area of land into different regions based on where rainfall flows down to. These regions are called drainage basins.

Given an elevation map (a 2-dimensional array of altitudes), label the map such that locations in the same drainage basin have the same label, subject to the following rules.

  • From each cell, water flows down to at most one of its 4 neighboring cells.
  • For each cell, if none of its 4 neighboring cells has a lower altitude than the current cell's, then the water does not flow, and the current cell is called a sink.
  • Otherwise, water flows from the current cell to the neighbor with the lowest altitude.
  • In case of a tie, water will choose the first direction with the lowest altitude from this list: North, West, East, South.

Every cell that drains directly or indirectly to the same sink is part of the same drainage basin. Each basin is labeled by a unique lower-case letter, in such a way that, when the rows of the map are concatenated from top to bottom, the resulting string is lexicographically smallest. (In particular, the basin of the most North-Western cell is always labeled 'a'.)

 

Input

The first line of the input file will contain the number of maps, T. T maps will follow, each starting with two integers on a line -- H and W -- the height and width of the map, in cells. The next H lines will each contain a row of the map, from north to south, each containing W integers, from west to east, specifying the altitudes of the cells.

Output

For each test case, output 1+H lines. The first line must be of the form

Case #X:

where X is the test case number, starting from 1. The next H lines must list the basin labels for each of the cells, in the same order as they appear in the input.

 

Limits

T ≤ 100;

Small dataset

1 ≤ H, W ≤ 10;
0 ≤ altitudes < 10.
There will be at most two basins.

Large dataset

1 ≤ H, W ≤ 100;
0 ≤ altitudes < 10,000.
There will be at most 26 basins.

这个题目其实是连通块标记问题, 如果水能从P0(x0,y0)流到p1(x1, y1),就说明p0和p1是连通的.

然后把所有的连通块标记出来, 最后按照从左到右,从上到下的顺序重新填写连通块的标记号码.

这个题目我是用类似于并查集的方法来标记连通块的, 最后再对块进行排序.

由于一开始没有排序,所以递交失败了,后来加上排序后,small set成功了,但是最后,成绩出来的时候, big set的结果不对,到现在还没找到问题.....

 

上代码(big set有问题):

#include <stdio.h>
#include <malloc.h>
#include <memory.h>

int SetVal(char* pVal, int* pPar, int nPos)
{
 if(pVal[nPos]==0)
 {
  pVal[nPos] = SetVal(pVal, pPar, pPar[nPos]);
 }
 return pVal[nPos];
}

int main()
{
 FILE* fp;
 int i, x, y;
 fp = fopen("1.txt", "r");

 int nRound = 0;
 fscanf(fp, "%d", &nRound);
 
 for(i=0; i<nRound; i++)
 {
  int nWidth, nHeight;
  fscanf(fp, "%d %d", &nHeight, &nWidth);

  int* pHei = (int*)malloc(nWidth*nHeight*sizeof(int));
  int* pPar = (int*)malloc(nWidth*nHeight*sizeof(int));
  char* pVal = (char*)malloc(nWidth*nHeight*sizeof(char));
  memset(pVal, 0, nWidth*nHeight*sizeof(char));
  int* pHeiT = pHei;
  for(y=0; y<nHeight; y++)
  {
   for(x=0; x<nWidth; x++, pHeiT++)
   {
    fscanf(fp, "%d", pHeiT);
   }
  }

  int nPos = 0;
  int nChar = 0;
  for(y=0; y<nHeight; y++)
  {
   for(x=0; x<nWidth; x++, nPos++)
   {
    int nHei = pHei[nPos];
    int nMinHei = 0xFFFFFF;
    int nMinPos;
    if(y>0)
    {
     if(pHei[nPos-nWidth]<nMinHei)
     {
      nMinHei = pHei[nPos-nWidth];
      nMinPos = nPos-nWidth;
     }
    }
    if(x>0)
    {
     if(pHei[nPos-1]<nMinHei)
     {
      nMinHei = pHei[nPos-1];
      nMinPos = nPos-1;
     }
    }
    if(x<nWidth-1)
    {
     if(pHei[nPos+1]<nMinHei)
     {
      nMinHei = pHei[nPos+1];
      nMinPos = nPos+1;
     }
    }
    if(y<nHeight-1)
    {
     if(pHei[nPos+nWidth]<nMinHei)
     {
      nMinHei = pHei[nPos+nWidth];
      nMinPos = nPos+nWidth;
     }
    }

    if(nMinHei<nHei)
    {
     pPar[nPos] = nMinPos;
    }
    else
    {
     pPar[nPos] = nPos;
     pVal[nPos] = 'a'+nChar;
     nChar++;
    }
   }
  }
  nPos = 0;
  for(y=0; y<nHeight; y++)
  {
   for(x=0; x<nWidth; x++, nPos++)
   {
    SetVal(pVal, pPar, nPos);
   }
  }
  
  char* pMask = (char*)malloc(nChar*sizeof(char));
  int* pMinPos = (int*)malloc(nChar*sizeof(int));
  for(x=0; x<nChar; x++)
  {
   pMask[x] = x;
   pMinPos[x] = 0xFFFFFF;
  }
  nPos = 0;
  for(y=0; y<nHeight; y++)
  {
   for(x=0; x<nWidth; x++, nPos++)
   {
    if(nPos<pMinPos[pVal[nPos]-'a'])
    {
     pMinPos[pVal[nPos]-'a'] = nPos;
    }
   }
  }

  for(y=0; y<nChar; y++)
  {
   for(x=0; x<nChar; x++)
   {
    if(pMinPos[y]<pMinPos[x])
    {
     int nTmp;
     nTmp = pMinPos[y];
     pMinPos[y] = pMinPos[x];
     pMinPos[x] = nTmp;
     nTmp = pMask[y];
     pMask[y] = pMask[x];
     pMask[x] = nTmp;
    }
   }
  }
  printf("Case #%d:/n", i+1);
  nPos = 0;
  for(y=0; y<nHeight; y++)
  {
   for(x=0; x<nWidth; x++, nPos++)
   {
    printf("%c ", pMask[pVal[nPos]-'a']+'a');
   }
   printf("/n");
  }

  free(pMask);
  free(pHei);
  free(pPar);
  free(pVal);
 }
 

 

 fclose(fp);
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值