The Die Is Cast

来源:互联网 发布:mac eclipse 安装svn 编辑:程序博客网 时间:2024/06/07 01:06

题干

总时间限制: 1000ms 内存限制: 65536kB
描述
InterGames is a high-tech startup company that specializes in developing technology that allows users to play games over the Internet. A market analysis has alerted them to the fact that games of chance are pretty popular among their potential customers. Be it Monopoly, ludo or backgammon, most of these games involve throwing dice at some stage of the game.
Of course, it would be unreasonable if players were allowed to throw their dice and then enter the result into the computer, since cheating would be way to easy. So, instead, InterGames has decided to supply their users with a camera that takes a picture of the thrown dice, analyzes the picture and then transmits the outcome of the throw automatically.

For this they desperately need a program that, given an image containing several dice, determines the numbers of dots on the dice.

We make the following assumptions about the input images. The images contain only three different pixel values: for the background, the dice and the dots on the dice. We consider two pixels connected if they share an edge - meeting at a corner is not enough. In the figure, pixels A and B are connected, but B and C are not.


A set S of pixels is connected if for every pair (a,b) of pixels in S, there is a sequence a1, a2, ..., ak in S such that a = a1 and b = ak , and ai and ai+1 are connected for 1 <= i < k.

We consider all maximally connected sets consisting solely of non-background pixels to be dice. `Maximally connected' means that you cannot add any other non-background pixels to the set without making it dis-connected. Likewise we consider every maximal connected set of dot pixels to form a dot.
输入
The input consists of pictures of several dice throws. Each picture description starts with a line containing two numbers w and h, the width and height of the picture, respectively. These values satisfy 5 <= w, h <= 50.

The following h lines contain w characters each. The characters can be: "." for a background pixel, "*" for a pixel of a die, and "X" for a pixel of a die's dot.

Dice may have different sizes and not be entirely square due to optical distortion. The picture will contain at least one die, and the numbers of dots per die is between 1 and 6, inclusive.

The input is terminated by a picture starting with w = h = 0, which should not be processed.
输出
For each throw of dice, first output its number. Then output the number of dots on the dice in the picture, sorted in increasing order.

Print a blank line after each test case.

样例输入
30 15
..............................
..............................
...............*..............
...*****......****............
...*X***.....**X***...........
...*****....***X**............
...***X*.....****.............
...*****.......*..............
..............................
........***........******.....
.......**X****.....*X**X*.....
......*******......******.....
.....****X**.......*X**X*.....
........***........******.....
..............................
0 0
样例输出
Throw 1
1 2 2 4
来源
Southwestern European Regional Contest 1998

题目的意思就是需要一个程序根据一张照片来得出照片中各个骰子的点数

简化为一个二维数组,像素简化为只有3中,‘.’代表背景,'*'代表骰子,'X'代表骰子上的点数

一块边与边相连互相联通的'X'和'*'就是代表照片中的骰子,一块联通的'X'代表的是一个点

做完之后看别人的都是用深搜吧,大概是递归的。我想的是遍历,按行从左到右遍历。之所以不用递归就是怕栈空间不够。。不过这道题不太可能。

递归的效率应该也是没有遍历好的吧。毕竟函数调用是有损耗的。


用遍历的话唯一保证的就是左边和上边的元素是遍历过的,为了标记已遍历到的非背景像素是属于哪个骰子的,将已遍历到的元素赋予全局唯一的值,可以使用一个int数组之类的来保存。同时要用map<全局标记,点数值>来储存骰子标号和点数对,当让用hash_map存的话,查找和插入值的速度会更快,然而vs2015不让用就算了,好像被取消了?。影响并不大。

当前遍历到的元素分三种情况

1.‘.’直接continue

2.'*'如果上边元素和左边元素都是'*'或者'X'那说明上面和左边,并且上面和左边标记为不同的骰子,那说明要把这两个骰子合起来看作一个骰子,将其合并并要更新对应数组中的标记值(这个得用递归了),这时候骰子的点数和应该是两个骰子点数的和

      如果左边和上面标记的是相同的骰子,那更新当前的元素的标记值为这个值就可以了就可以了

      如果左边和上面标记只有一个被标记为骰子,那么更新当前元素的标记值为那个值

      如果左边和上面没有,则认为是一个新的骰子,分配全局唯一标记,点数为0,存入map

3.‘X’其标记的操作更新操作月'*'是相同的,其实对于区分骰子的工作来说'X'和‘*’是一样的,他们都是骰子的一部分

    不同的是'X'需要考虑'X'左边和上面存在'X'的情况

    假如有个'X'说明这个点其实属于前面被遍历过的,不需要跟新相应骰子标号和点数对

     如果左边上面没有'X'有'*'的话,那就是一个已登记的骰子中出现的新的点数,更新对应值加一,如果都是'*',一个新的骰子并点数为1

    还有就是与两个'X'相邻,那么可能需要合并两个点数,那么最后这个元素所属的骰子反而要自减1,当然如果是以下情况


     X***............
  XXX***...........
XXXX**............
其中红色的在遍历到的时候,可能会认为要将对应的骰子的点数自减1,但是其实这个骰子上的点并不是在红色这个像素点合并的,而是在其左边的时候就已经可以合并 了。这个问题其实是因为分割点数和分割骰子是同样的问题,然而我却想在分割骰子的同时去分割点数。

最好的做法是去用相同的一个map来标记点,或者两遍处理。

然而我用了一个不健全的做法,如果遍历到i,j 并且i-1,j  和 i, j-1都是'X',那么如果i-1,'j-1'是'X'的话,那么我就认为不需要自减一,因为我认为至少是在i,j-1之前(包括自己)做的第一次点数合并,但是如果出现

XXX

X*X

XXX

的情况,那这种其实就出错了。但是想想一张照片中一个黑点中,如果中间有个像素是白的也是不太能吧,大概就是不会出现这种不连续的情况,我就用粗糙的判断了,当然也可以用递归深搜的方法去看左边和右边的点在已遍历的点中能否有连通的路径。也可以预处理,在分割骰子前,现分割点,对于有点的地方,一团'X'只用一个'X'表示就好了,就不用考虑这种情况了。

代码如下

#include <iostream>#include <vector>#include <map>#include <queue>using namespace std;int totalNum = 0;int dotNum = 0;class cmp {    public: bool operator()(int a, int b) {return a > b;}};void merge(char(*img)[55], int x, int y, int newNum, int oldNum) {if (img[x][y] - '0' != oldNum) {return;}img[x][y] = newNum + '0';if (x - 1 >= 0) {merge(img, x - 1, y, newNum, oldNum);}if (y - 1 >= 0) {merge(img, x, y - 1, newNum, oldNum);}return;}void handle(char (*raw)[55], char (*img)[55], int w, int h, map<int, int> & result) {for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {if (img[i][j] == '.') continue;if (img[i][j] == '*') {int preNum = -1;if (i - 1 >= 0) {if (img[i - 1][j] != '.') {preNum = img[i - 1][j] - '0';}}if (j - 1 >= 0) {if (img[i][j - 1] != '.') {if (preNum == -1) {preNum = img[i][j - 1] - '0';}if (preNum != -1 && preNum != img[i][j - 1] - '0') {//need mergeint mergeNum = img[i][j - 1] - '0';merge(img, i, j - 1, preNum, mergeNum); int dotNum = result.at(mergeNum);result[mergeNum] = -1;result[preNum] += dotNum;}}}// new diceif (preNum == -1) {img[i][j] = '0' + totalNum;result[totalNum] = 0;totalNum++;}else {img[i][j] = '0' + preNum;}}if (img[i][j] == 'X') {int preNum = -1;int connect_dot = 0;if (i - 1 >= 0) {if (img[i - 1][j] != '.') {preNum = img[i - 1][j] - '0';if (raw[i - 1][j] == 'X') {connect_dot++;}}}if (j - 1 >= 0) {if (img[i][j - 1] != '.') {if (raw[i][j - 1] == 'X') {connect_dot++;}if (preNum == -1) {preNum = img[i][j - 1] - '0';}if (preNum != -1 && preNum != img[i][j - 1] - '0') {//need mergeint mergeNum = img[i][j - 1] - '0';merge(img, i, j - 1, preNum, mergeNum);int dotNum = result.at(mergeNum);result[mergeNum] = -1;result[preNum] += dotNum;}}}// new diceif (preNum == -1) {img[i][j] = '0' + totalNum;result[totalNum] = 1;totalNum++;}// update dot numbers;else {if (connect_dot == 0){result[preNum] += 1;}else if (connect_dot == 2 && raw[i-1][j-1]!='X'){result[preNum] -= 1;}img[i][j] = '0' + preNum;}}}}}int main() {int w, h;int caseNum = 0;char img[55][55];char raw[55][55];cin >> w >> h;while (true) {caseNum++;for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {cin >> img[i][j];raw[i][j] = img[i][j];}}map<int, int> result;priority_queue<int, vector<int>, cmp> dot_num;handle(raw, img, w, h, result);for (int i = 0; i < totalNum; i++) {if (result[i] != -1) {dot_num.push(result[i]);}}cout << "Throw " << caseNum<< endl;while (true) {cout << dot_num.top();dot_num.pop();if (dot_num.empty()) break;else cout << " ";}cin >> w >> h;totalNum = 0;cout << endl << endl;if (w == 0 && h == 0) break;//else cout << endl;}return 0;}

最后发现oj不仅输入不够多和严格,时间和空间要求也不严格。倒是要求每个例子的输出要输出两个回车。我也是醉了

大概看了一下别人的结果,这个内存消耗还是不大的,时间不得而知,如果不用如果分两次遍历分割,并且用hash的方法存结果,那么遍历数组的时间应该是线性的,可能的变数在于合并两个已知的骰子标记,还有排序输出结果吧。其中影响最大的应该是合并操作,如果把需要替换的标记的元素的上下标也用hash的方式去存储起来而不用去搜索遍历的话那肯定会更快了,不过这空间换时间就看怎么个想法了

其他做法

http://www.cnblogs.com/liaoguifa/archive/2013/03/28/2987144.html

0 0