[Liiurujia Birthday Match] Q 调试了将近7h的题目

Problem Q

Quall[e]? Quale?

URL:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=12&page=show_contest&contest=312&mosmsg=Submission+received

Attantion: 

目前为止全世界加上我只有仨人过啊目前为止全世界加上我只有仨人过啊目前为止全世界加上我只有仨人过啊目前为止全世界加上我只有仨人过啊

ProblemSet:

If you speak German, you should know the first word in the title. If you speak Italian, you should know the second one :)

Why I use these two words? Because I don't have better choices :( I want my problems' titles to start with A, B, C, etc. This is problem Q so it has to begin with the letter Q.

Sometimes it's difficult. Each problem title has to tell people something about the problem itself, so it can't be arbitrary. If I can't find a suitable title in English, I have to try other languages like Chinese (for example, see the last three problems in Rujia Liu's Present 5 ^_^).

Here is an example.

No English French Chinese
1 A B C
2 D - B
3 C B -
4 E - E
5 C A -

The title of problem 1 in English starts with A, and the French version starts with B. The Chinese title of problem 1 starts with C. A hyphen means "N/A", so problem 2 doesn't have a French version.

One possible combination is (note that each problem should be used exactly once):

Problem A Problem 1 in English
Problem B Problem 3 in French
Problem C Problem 5 in English
Problem D Problem 2 in English
Problem E Problem 4 in Chinese

Could you tell me all the possible language combinations? For each combination, all the languages in it must be used (i.e. You can't say the combination is {English,Chinese} if none of the problems actually used the Chinese version).

Input

The first line contains the number of test cases T(T<=500). Each test case begins with two integers nm(3<=n<=26, 1<=m<=5), the number of problems and the number of languages. The next n lines contains the table containing the first m upper-case letters or '-'. The j-th column in the i-th row in the table is the first letter of the title in the j-th language. A special character '-' means that version does not exist.

Output

For each test case, print all possible combinations in a single line. Each combination is a string of languages used (languages are labeled 1 to m) in increasing order. Shorter combinations should come first. Combinations of the same length should be sorted in increasing order. If the problemset cannot be made, print -1.

Sample Input

4
5 3
ABC
D-B
CB-
E-E
CA-
3 2
AB
C-
-C
4 4
-A--
BB--
--CC
--D-
3 4
AAAA
BBBB
CCCC

Output for the Sample Input

Case 1: 12 123
Case 2: -1
Case 3: 23 123 234 1234
Case 4: 1 2 3 4 12 13 14 23 24 34 123 124 134 234

问题描述:

给入N * M 的字母和符号矩阵,stri][j] 代表 第 i 个问题 的 第 j 种描述 是 str[i][j] , 问——一共有多少个列的组合,能保证每行取一个数,列都能取到,A~A+N都能取到。


思路历程:

首先听了岛娘的想法直接想到了DLX。一开始想的是把所有的答案都搜出来,然后用解来判断有哪些列满足。TLE。

后来想到了,枚举所有解,然后用DLX判断是否有解。TLE很久了之后嘟嘟嘟嘟大神告诉我我题目读错了,唤起信心继续TLE。但是本机已经能跑完600+个数据无压力。于是尝试着加了一个剪枝之后wa!!!瞬间信心满满。搞了很久之后重新读题+重新看代码。发现一个很简单的地方写错了。改了之后就Accepted了。


解法:

1、预处理所有的列组合,一共31个。

2、预处理所有列组合的分解,比如123 分解成 1 、 2 、 3等等

3、枚举所有列组合,枚举图,然后建DLX图:

第 X 行 代表 第 p 个题目 用第 q 个描述 。

第 1 ~ n 列代表所有的字母,n + 1 列 ~ 2 * n 列代表每一行当且仅当用一次,两个限制。

4、DLX判断是否有解,如果有解则输出。

5、否则输出-1

P.S. 因为每一列都要枚举到,本来应该是前一半用精确覆盖后一半用可重复覆盖。但是如下优化就可以完全用精确覆盖:

在进入 k + 1 层之前, 如果 枚举的列数 - 现在用的列数 > 还剩下的深度(也就是不够枚举了) 那么直接返回 false不进入下一层!!及其重要的剪枝!!!!!!

剪枝:

1、读入图的时候,如果A ~ A + N - 1 的字母不全 , 那么输出-1 !!! 重要剪枝!!!

2、读入图的时候,如果 A + N ~ Z 有字母,那么输出 -1!!! 重要剪枝!!!

3、为了防止mat 数组的reset , 将mat数组设置为int , 加入int ILoveMZ , 每次要更新mat的时候 ++ ILoveMZ , 然后将mat更新元素替换为 ILoveMZ 于是如果当前元素不为ILoveMZ 就不是这次更新的。。。这个小技巧让代码快了 1s


————————————————————整个题目解法到这里就结束了————————————————————

Code:

#define LOCAL

/** ` Micro Mezzo Macro Flation -- Overheated Economy ., Ver 0.1 **/

#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>

using namespace std;

// <<= ' 0. I/O Accelerator interface .,

template<class T> inline T& RD(T &x){
    //cin >> x;
    scanf("%d", &x);
    //char c; for (c = getchar(); c < '0'; c = getchar()); x = c - '0'; for (c = getchar(); '0' <= c && c <= '9'; c = getchar()) x = x * 10 + c - '0';
    //char c; c = getchar(); x = c - '0'; for (c = getchar(); c >= '0'; c = getchar()) x = x * 10 + c - '0';
    return x;
}
typedef vector<int> VI;
#define RST(x) memset(x , 0 , sizeof(x))
/* .................................................................................................................................. */
const int maxN = 26 * 5 + 1 , maxM = 26 + 26 + 1;
const int max_size = maxN * maxM;
const int inf = 0x3f3f3f3f;
int L[max_size], R[max_size], U[max_size], D[max_size], CH[max_size], RH[max_size];
int S[maxM], O[maxM];
int head, size;
int node(int up, int down, int left, int right) {
    U[size] = up, D[size] = down;
    L[size] = left, R[size] = right;
    D[up] = U[down] = R[left] = L[right] = size;
    return size++;
}
int ILoveMZ;
int mat[maxN][maxM];
void init(int N, int M) {
    size = 0;
    head = node(0, 0, 0, 0);
    for (int j = 1; j <= M; ++j) {
        CH[j] = node(size, size, L[head], head), S[j] = 0;
    }
    for (int i = 1; i <= N; ++i) {
        int row = -1, k;
        for (int j = 1; j <= M; ++j) {
            if (mat[i][j] != ILoveMZ) continue;
            if (row == -1) {
                row = node(U[CH[j]], CH[j], size, size);
                RH[row] = i, CH[row] = CH[j], ++S[j];
            } else {
                k = node(U[CH[j]], CH[j], L[row], row);
                RH[k] = i, CH[k] = CH[j], ++S[j];
            }
        }
    }
}
void remove(const int c) {
    L[R[c]] = L[c], R[L[c]] = R[c];
    for (int i = D[c]; i != c; i = D[i]) {
        for (int j = R[i]; j != i; j = R[j]) {
            U[D[j]] = U[j], D[U[j]] = D[j];
            --S[CH[j]];
        }
    }
}
void resume(const int c) {
    for (int i = U[c]; i != c; i = U[i]) {
        for (int j = L[i]; j != i; j = L[j]) {
            ++S[CH[j]];
            U[D[j]] = D[U[j]] = j;
        }
    }
    L[R[c]] = R[L[c]] = c;
}
int len;
int rname[300][4];
int cline , pline;
int n , m;
int now;

int canoc[12347][9];
int cntcanoc[12347];
bool useCo[9];
int cntuseCo;
int cntCoNow;
bool DLX(const int k) {
    if (R[head] == head) {
        len = k - 1;
        return true;
    }
    int s = inf, c;
    for (int t = R[head]; t != head; t = R[t]) {
        if (S[t] < s) s = S[t], c = t;
    }
    remove(c);
    for (int i = D[c]; i != c; i = D[i]) {
        O[k] = RH[i];
        for (int j = R[i]; j != i; j = R[j]) {
            remove(CH[j]);
        }
        bool change = useCo[rname[O[k]][1]];
        if (!useCo[rname[O[k]][1]]) ++ cntuseCo;
        useCo[rname[O[k]][1]] = 1;
        if (n - k - 1>= cntCoNow - cntuseCo && DLX(k + 1)) {
            return true;
        }
        if (!change){
            --cntuseCo;
            useCo[rname[O[k]][1]] = false;
        }
        for (int j = L[i]; j != i; j = L[j]) {
            resume(CH[j]);
        }
    }
    resume(c);
    return false;
}
int _;
char str[50][50];

bool use[50];
int pp[50];
const int trynumber[] = {31,1,2,3,4,5,12,13,14,15,23,24,25,34,35,45,123,124,125,134,135,145,234,235,245,345,1234,1235,1245,1345,2345,12345};

void occupy(int x){
    int y = x;
    cntcanoc[y] = 0;
    for (;x;x /= 10){
        canoc[y][cntcanoc[y]++] = (x % 10);
    }
}
void inittable(){
    for (int i = 1 ; i <= 31 ; ++i)
        occupy(trynumber[i]);
}
bool hasans;
int numberWord;
void solve(){
    scanf("%d%d" , &n , &m);
    RST(use);
    for (int i = 0 ; i < n ; ++i){
        while(gets(str[i]) , strlen(str[i]) < m);
        for (int j = 0 ; j < m ; ++j){
            if (str[i][j] < 'A' || str[i][j] > 'Z') continue;
            //if (!use[str[i][j] - 'A']) cout << str[i][j] << endl;
            use[str[i][j] - 'A'] = 1;
        }
    }
    printf("Case %d:" , ++_);
    numberWord = 0;
    for (int i = 0 ; i < 26 ; ++i){
        //printf("%c" , use[i]);
        if (!use[i] && i < n) {
            printf(" -1\n");
            return;
        }
        if (i < n)pp[i] = numberWord ++;
        if (use[i] && i >= n){
            printf(" -1\n");
            return;
        }
    }
    //cout << numberWord << endl;
    hasans = 0;
    for (int T_ = 1 ; T_ <= 31 ; ++T_){
        now = trynumber[T_];
        if (now % 10 > m) continue;
        //RST(mat);
        ++ILoveMZ;
        cline = 0;
        int cColumn = 2 * n;
        cntCoNow = cntcanoc[now];
        for (int i = 0 ; i < n ; ++i){
            for (int T_T = 0 ; T_T < cntCoNow ; ++T_T){
                int j = canoc[now][T_T] - 1;
                if (str[i][j]< 'A' || str[i][j] > 'Z') continue;
                ++cline;
                rname[cline][0] = i;
                rname[cline][1] = j;
                mat[cline][pp[str[i][j] - 'A'] + 1] = ILoveMZ;
                mat[cline][n + i + 1] = ILoveMZ;
                //printf("%d %d %d\n" , i , j , pp[str[i][j] - 'A'] + 1);
            }
        }
        pline = cline;/*
        for (int i = 0  ; i < SZ(use) ; ++i){
            mat[++pline][i + 1] = 1;
        }*/
        init(pline , cColumn);
        RST(useCo);
        cntuseCo = 0;
        if(DLX(0)){
            hasans = 1;
            printf(" %d" , now);
        }
    }
    if (!hasans) printf(" -1\n");
    else{
        printf("\n");
    }
}
#define Rush int T____; RD(T____); DO(T____)
#define DO(n) for ( int ____n ## __line__ = n; ____n ## __line__ -- ; )
int main(){
    //freopen("input.txt" , "r" , stdin);
    //freopen("output.txt" , "w" , stdout);
    ILoveMZ = 0;
    RST(mat);
    _ = 0;
    inittable();
    Rush solve();
}


感想:

第一次写这么恶心的题目,不过对于这种题目已经有了自己的心得。有了胆量,有了基友们的支持,终于过掉了此题。感觉很好,调试了奖金7h的题目,终于过掉长舒一口气呢。各位晚安~~~

谢谢妹子孟竹始终给我支持~ILoveYou

你可能感兴趣的:([Liiurujia Birthday Match] Q 调试了将近7h的题目)