2018 East Central Regional Contest/ ECNA2018 E.The Punctilious Cruciverbalist

Problem Description

A crossword puzzle consists of a grid of squares in which intersecting words are placed, one letter per square. Some grid squares are filled black indicating that no letter should go there. Solvers are given clues for each word, and each clue is identified by a starting clue number and either the word Across (for horizontal words) or Down (for vertical words). Each square that can start a word is given a clue number, starting in the upper left and proceeding left-to-right, row-wise. A square will contain an across-clue number if the square to its left is either black or off the grid, and a square will contain a down-clue number if the square above it is either black or off the grid. See Figure E.1 for a simple example.

Will Longz is an avid crossword puzzle enthusiast and as with most solvers, he first tries to solve clues where some of the letters are already present (from already solved clues whose answers intersect the clue he is working on). Typically, the closer to the beginning of the word that these letters are, the better. Will has developed a metric to help him decide the order in which he should solve clues. If the answer to a clue spans n squares, he assigns the value n to the first of these squares, n − 1 to the second square, and so on ending with assigning 1 to last square. The rank of any unsolved clue is then defined as follows: for each square that already has a letter in it, add the value assigned to that square to a running total. Then divide this total by the sum of all the values assigned to that clue to get that clue’s rank. Once this is done for all the clues, Will solves the clue with the highest rank. If there is a tie, he selects an across clue over a down clue. If there is still a tie he picks the clue with the smallest starting clue number. After each clue is solved Will recalculates the rankings before selecting the next highest ranked clue.

As an example, consider the crossword in Figure E.1 where the 6-Across clue has already been solved (we’ll used 6A as a shorter specification for this clue). To determine the rank of the 1-Down (1D) clue, Will first assigns the values 3, 2 and 1 to each of the squares going down. Since the last square has a letter in it, the ranking of this clue is 1/(3 + 2 + 1) = 1/6. In a similar fashion he determines that the ranking of clues 2D and 3D are 2/10 and the ranking of 5D is 2/6. The rankings of the three unsolved across clues 1A, 4A and 7A are all 0. Thus the next clue to solve is 5D (we’ll be optimistic and always assume Will can solve any clue). Once this is solved, the rankings of 4A and 7A become 1/10 and 1/6, respectively. Since there is now a tie between the highest ranking clues (2D and 3D) and they are both down clues, Will picks the down clue with the lowest clue number and solves 2D. Proceeding similarly, the remaining clues are solved in the following order (with their ranking at time of solution in parentheses): 7A (4/6), 4A (4/10), 3D (6/10) and 1A (3/6). Note that clue 5D is not on the list, as it is completely solved by the previously solved clues (namely 4A, 6A and 7A).
在这里插入图片描述
Figure E.1: Example crossword puzzle.

For this problem, you will be given a crossword puzzle grid with zero or more squares already filled in and you must determine the order in which unsolved clues should be solved. The filled-in squares may or may not correspond to completely solved clues. One final twist: we will only give you the black squares and letters in the puzzle. You must determine the clue numbers (I bet Will could do it!).

Input
Input starts with a line containing two integers r c (1 ≤ r, c ≤ 50) specifying the number of rows and columns in the crossword grid. Following this are r lines each containing c characters which describe the puzzle. Each of these characters is either a ‘.’ (indicating an empty square), a ‘#’ (indicating a black square), or an uppercase alphabetic English character (indicating a solved square). There will always be at least one empty square in the grid.

Output
Display the order in which clues should be solved one per line, using the metric described above. Each clue should start with the clue number followed by either the letter ‘A’ or ‘D’ (for an across or down clue).

Sample Input

4 4
…#

JAVA
#…

Sample Output

5D
2D
7A
4A
3D
1A

Solution

 每次填好的时候只选择受影响的行列的Clue的Ranking进行更新来加速,其他的直接模拟就好
挺多容易写错的地方。。。。例如Clue的编号,相同ranking时哪个优先等问题。。
写这道题时因为判断当前Clue是否填满的语句写错了,导致没能AC,着实遗憾
检查了半天 将:
在这里插入图片描述
改为:
在这里插入图片描述
就AC了。。。


AC Code

#include<bits/stdc++.h>

using namespace std;
#define BLOCK 2
#define DATA 1
const int MAX_N = 55;
int r, c, clueNum = 0, orgClueNum, idCnt = 1;
int board[MAX_N][MAX_N];
char foo[MAX_N];

class Clue;

vector<Clue *> inRow[55];
vector<Clue *> inCol[55];

void updateRow(int row);

void updateCol(int col);

bool inBound(int i, int j) { return i >= 0 && j >= 0 && i < r && j < c; }

bool isNothing(int i, int j) { return inBound(i, j) ? (board[i][j] == BLOCK) : true; }

class FS {
    public:
        int fz{};
        int fm{};

        FS() = default;

        FS(int z, int m) {
            fz = z;
            fm = m;
            if (fm == 0)
                fm = 1;
        }

        bool operator<(FS const &o) const { return 1LL * fz * o.fm < 1LL * fm * o.fz; }

        bool operator==(FS const &o) const { return 1LL * fz * o.fm == 1LL * fm * o.fz; }
};

class Clue {
    public:
        int id{};
        int i{}, j{};
        bool isD{};
        FS ranking;

        Clue() = default;

        Clue(int id, int i, int j, bool isD) : id(id), i(i), j(j), isD(isD) {}

        bool operator<(Clue const &o) const { return ranking == o.ranking ? (isD == o.isD ? id > o.id : isD) : ranking < o.ranking; }

        void setVal() {
            int num = 0, res = 0;
            if (!isD) {
                for (int jj = j; !isNothing(i, jj); ++jj)
                    ++num;
                int foo = num;
                for (int jj = j, cnt = 0; cnt < foo; ++jj, ++cnt, --num)
                    if (board[i][jj])
                        res += num;
                ranking = FS(res, (foo + 1) * foo / 2);
            } else {
                for (int ii = i; !isNothing(ii, j); ++ii)
                    ++num;
                int foo = num;
                for (int ii = i, cnt = 0; cnt < foo; ++ii, ++cnt, --num)
                    if (board[ii][j])
                        res += num;
                ranking = FS(res, (foo + 1) * foo / 2);
            }
        }

        void fillAndUpdate() {
            if (!isD) {
                for (int jj = j; !isNothing(i, jj); ++jj) {
                    board[i][jj] = DATA;
                    updateCol(jj);
                    updateRow(i);
                }
            } else {
                for (int ii = i; !isNothing(ii, j); ++ii) {
                    board[ii][j] = DATA;
                    updateRow(ii);
                    updateCol(j);
                }
            }
        }

        void display() {
            printf("%d", id);
            if (isD)
                putchar('D');
            else putchar('A');
            putchar('\n');
        }
} clues[5005];

void updateRow(int row) {
    for (Clue *c: inRow[row])
        if (c->ranking.fz != c->ranking.fm)
            c->setVal();
}

void updateCol(int col) {
    for (Clue *c: inCol[col])
        if (c->ranking.fz != c->ranking.fm)
            c->setVal();
}

void addClue(int i, int j, bool isD) {
    clues[clueNum] = Clue(idCnt, i, j, isD);
    inRow[i].push_back(&clues[clueNum]);
    inCol[j].push_back(&clues[clueNum]);
    ++clueNum;
}

void init() {
    for (int i = 0; i < clueNum; ++i)
        clues[i].setVal();
}

Clue *findMax() {
    Clue *res = nullptr;
    for (int i = 0; i < orgClueNum; ++i) {
        if (clues[i].ranking.fz == clues[i].ranking.fm)
            continue;
        if (res == nullptr)
            res = &clues[i];
        else if ((*res) < clues[i])
            res = &clues[i];
    }
    return res;
}

int main() {
    cin >> r >> c;
    for (int i = 0; i < r; ++i) {
        cin >> foo;
        for (int j = 0; j < c; ++j) {
            if (foo[j] == '#')
                board[i][j] = BLOCK;
            else if (foo[j] != '.')
                board[i][j] = DATA;
        }
    }
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            bool isClue = false;
            if (isNothing(i - 1, j) && board[i][j] != BLOCK) {
                addClue(i, j, true);
                isClue = true;
            }
            if (isNothing(i, j - 1) && board[i][j] != BLOCK) {
                addClue(i, j, false);
                isClue = true;
            }
            if (isClue)
                ++idCnt;
        }
    }
    init();
    orgClueNum = clueNum;
    while (true) {
        Clue *c = findMax();
        if (c == nullptr)
            break;
        c->display();
        c->fillAndUpdate();
    }
    return 0;
}
/*
4 4
...#
....
JAVA
#...


2 2
.A
.B
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值