2012-05-27 18:25:39Morris

[UVA][MST] 1235 - Anti Brute Force Lock

Lately, there is one serious problem with Panda Land Safe Box: several safes have been robbed! The safes are using old 4-digits rolling lock combination (you only have to roll the digit, either up or down, until all four of them match the key). Each digit is designed to roll from 0 to 9. Rolling-up at 9 will make the digit become 0, and rolling-down at 0 will make the digit become 9. Since there are only 10000 possible keys, 0000 through 9999, anyone can try all the combinations until the safe is unlocked.

epsfbox{p4138.eps}

What's done is done. But in order to slow down future robbers' attack, Panda Security Agency (PSA) has devised a new safer lock with multiple keys. Instead of using only one key combination as the key, the lock now can have up to N keys which has to be all unlocked before the safe can be opened. These locks works as the following:

  • Initially the digits are at 0000.
  • Keys can be unlocked in any order, by setting the digits in the lock to match the desired key and then pressing the UNLOCK button.
  • A magic JUMP button can turn the digits into any of the unlocked keys without doing any rolling.
  • The safe will be unlocked if and only if all the keys are unlocked in a minimum total amount of rolling, excluding JUMP (yes, this feature is the coolest one).
  • If the number of rolling is exceeded, then the digits will be reset to 0000 and all the keys will be locked again. In other word, the state of the lock will be reset the cracking is failed.


PSA is quite confident that this new system will slow down the cracking, giving them enough time to identify and catch the robbers. In order to determine the minimum number of rolling needed, PSA wants you to write a program. Given all the keys, calculate the minimum number of rolls needed to unlock the safe.

Input 

The first line of input contains an integer T , the number of test cases follow. Each case begins with an integer N (1$ le$N$ le$500) , the number of keys. The next N lines, each contains exactly four digits number (leading zero allowed) representing the keys to be unlocked.

Output 

For each case, print in a single line the minimum number of rolls needed to unlock all the keys.


Explanation for the 2nd case:

  • Turn 0000 into 1111, rolls: 4
  • Turn 1111 into 1155, rolls: 8
  • Jump 1155 into 1111, we can do this because 1111 has been unlocked before.
  • Turn 1111 into 5511 rolls: 8

Total rolls = 4 + 8 + 8 = 20

Sample Input 

4 
2 1155 2211 
3 1111 1155 5511 
3 1234 5678 9090 
4 2145 0213 9113 8113

Sample Output 

16 
20 
26 
17

題目意思: 從 0000 開始, 藉由轉換, 將所有密碼全解開 的最少次數

做法 : 
很明顯的是一題 MST 問題, 但 0000 開始卻成了障礙, 因為 0000 可能不是一個已解開的密碼,
因此不能放在 JUMP 的指令, 為解決這個問題, 我們先不理 0000, 最後再找最少次數的地方接上去即可,

由於邊的權值很小, 用快排顯得有點慢了, 採用基數排序

#include <stdio.h>
#include <stdlib.h>
struct edge {
    int x, y, v;
};
edge D[130000], E[130000];
int p[501], r[501];
void init(int n) {
    int i;
    for(i = 0; i <= n; i++)
        p[i] = i, r[i] = 1;
}
int find(int x) {
    return p[x] == x ? x : (p[x]=find(p[x]));
}
int joint(int x, int y) {
    x = find(x), y = find(y);
    if(x != y) {
        if(r[x] > r[y])
            r[x] += r[y], p[y] = x;
        else
            r[y] += r[x], p[x] = y;
        return 1;
    }
    return 0;
}
int sort(int n) {
    int w[40] = {}, i;
    for(i = 0; i < n; i++)
        w[D[i].v]++;
    for(i = 1; i < 40; i++)
        w[i] += w[i-1];
    for(i = 0; i < n; i++)
        E[--w[D[i].v]] = D[i];

}
int main() {
    int t, n, tmp;
    char s[501][5] = {"0000"};
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        int i, j, k, sum, m = 0;
        for(i = 1; i <= n; i++) {
            scanf("%s", s[i]);
            for(j = 1; j < i; j++) {
                sum = 0;
                for(k = 0; k < 4; k++) {
                    tmp = abs(s[i][k]-s[j][k]);
                    if(tmp > 5) tmp = 10-tmp;
                    sum += tmp;
                }
                D[m].x = i, D[m].y = j, D[m].v = sum;
                m++;
            }
        }
        sort(m);
        init(n);
        int ans = 0, cnt = 0;
        for(i = 0; i < m; i++) {
            if(joint(E[i].x, E[i].y)) {
                ans += E[i].v;
                cnt++;
                if(cnt == n)
                    break;
            }
        }
        int link = 40;
        for(i = 1; i <= n; i++) {
            sum = 0;
            for(k = 0; k < 4; k++) {
                tmp = abs(s[0][k]-s[i][k]);
                if(tmp > 5) tmp = 10-tmp;
                sum += tmp;
            }
            if(sum < link)
                link = sum;
        }
        printf("%d\n", ans+link);
    }
    return 0;
}