PKU Campus 2016 I:PKU Zealots(模拟)

PKU Campus 2016 I:PKU Zealots(模拟)
总时间限制: 1000ms 内存限制: 262144kB

描述
PKU zealots are those who love PKU very much. Argo is one of them. Even more, his love for PKU extends to the letters ”P”, ”K” and ”U”. As a result, he practices writing these three letters every day. He always uses Arial, which is a font without small projecting features call ”serifs” at the end of strokes.

One day, Argo hears that some AI programs can recognize letters. He does not believe that AIs are able to recognize the letters he wrote down. Please write a program to counter him.

Given a picture in the form of pixel matrix, you are required to count the number of each letter. It is guaranteed that every character appears in the picture is one of the three letters. The letters will not be too small to recognize (not smaller than the letter ”K” in the Sample Input). The strokes of different letters will be at least two pixels away from each other. Note that letters may be written in different sizes and directions.

输入

The first line contains an integer T (1 ≤ T ≤ 5), indicating the number of test cases.

For each test case:

The first line contains two integers N and M (1 ≤ N,M ≤ 1500), indicating the numbers of rows and columns of the pixel matrix;

Then follows N lines with a string of length M on each, indicating a N*M pixel matrix. The pixel matrix is only consist of ”.”s and ”#”s. The ”#”s make up the strokes of the letters.

输出

For each test case, output four lines:

The first line contains a letter ”P”, a colon ”:”, a space, and the number of ”P”s;

The second line contains a letter ”K”, a colon ”:”, a space, and the number of ”K”s;

The third line contains a letter ”U”, a colon ”:”, a space, and the number of ”U”s;

The last line contains a word ”total”, a colon ”:”, a space, and the total number of letters.

样例输入

1
69 60
(字符图略)

样例输出

P: 1
K: 1
U: 2
total: 4

本题简直是一个开脑洞的题目,要求识别PKU三个字母。

首先识别P比较容易,只需要看全局连通空白区域减去一即可。

那么接下来的突破口是识别出K或者U,因为知道总是也很容易,只需要看字母连通块个数即可。那么是识别K还是U,它们有什么特点呢?当时考完ACM尝试做过一些方法,但是没有成功,直到后来看到一个point:K的重心离K较近,而U,P重心在图形外面。这个point配合调参(参数类似所谓的周围是多少之类的)就可以过。真是脑洞大开的一个题目啊。

理论上本题方法有很多,只要特征得当应该都可以区分三个字母。可惜时间卡得太死了,常数大一点就会TLE。所以一些相对精确的高级算法不仅缺少测试集,而且一定会TLE,也根本写不出来。但是像我的代码这种弱一点的特征又很不精确,依赖于数据强弱。所以本题关键在于找到合适的特征,也就是那个作者设计的特征,才能卡过数据。所以本题似乎有一些玄学成分。


Accepted    30080kB     265ms   1911 B  G++     
#include<stdio.h>
#include<math.h>

const int SIZE = 1500; 
const int dir[8][2] = {
    {1, 0}, {-1, 0}, {0, 1}, {0, -1}, 
    {-1, 1}, {1, 1}, {1, -1}, {-1, -1}};

const double RATION = 0.1;
const int D = 1;

int cases, n, m;
int map[SIZE+5][SIZE+5];
//-4: border; -3-> -2: blank; -1: block; 1, 2, 3, ... : code of block;

int P, K, UP, N;
int sum_i, sum_j, area, ave_i, ave_j;
double ration;
char ch;

void bfs_P(int i, int j)
{
    map[i][j] = -2;
    for (int t = 0;t < 4; t++)
        if (map[i + dir[t][0]][j + dir[t][1]] == -3)
            bfs_P(i + dir[t][0],j + dir[t][1]);
    return;
}

void bfs_total(int i, int j)
{
    map[i][j] = N;
    area++;
    sum_i += i;
    sum_j += j;
    for (int t = 0; t < 8; t++)
        if (map[i + dir[t][0]][j + dir[t][1]] == -1)
            bfs_total(i + dir[t][0], j + dir[t][1]);
    return;
}

int main()
{
    scanf("%d\n", &cases);
    while (cases--)
    {
        P = -1;
        K = UP = N = 0;
        scanf("%d %d\n", &n, &m);
        for (int i = 0; i <= n + 1; i++)
            map[i][m + 1] = map[i][0] = -4;
        for (int j = 0; j <= m + 1; j++)
            map[n + 1][j] = map[0][j] = -4;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                scanf("%c", &ch);
                map[i][j] = (ch == '#') ? -1 : -3;
            }
            scanf("\n");
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (map[i][j] == -3)
                {
                    P++;
                    bfs_P(i, j);
                }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (map[i][j] == -1)
                {
                    N++;
                    area = sum_i = sum_j = 0;
                    bfs_total(i,j);
                    ave_i = (int) ((double) sum_i)/area;
                    ave_j = (int) ((double) sum_j)/area;
                    ration = 0;
                    for (int i1 = ave_i - D; i1 <= ave_i + D; i1++)
                        for (int j1 = ave_j - D; j1 <= ave_j + D; j1++)
                            if (map[i1][j1] == N)
                                ration += 1.0 / ((2 * D + 1) * (2 * D + 1));
                    if (ration > RATION)
                        K++;
                    else
                        UP++;
                }
        printf("P: %d\nK: %d\nU: %d\ntotal: %d\n", P, K, UP - P, N);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值