Sicily 2375. Scanning UPC Barcodes

2375. Scanning UPC Barcodes

Constraints

Time Limit: 1 secs, Memory Limit: 256 MB

Description

The UPC-A bar code encodes 12 decimal digits in alternating ``dark" and ``light" bars as 15 patterns  SLLLLLLMRRRRRRE where  S is the start pattern  101 (1 indicates ``dark" and 0 indicates ``light"),  M is the middle pattern  01010 and  E is the end pattern  101. Each  L is a left pattern corresponding to one of the first 6 digits and each  R is a right pattern corresponding to one of the last 6 digits. The width of each bar is a multiple of a fixed value (the  Xdimension). Again a 1 indicates a ``dark" band and 0 indicates a ``light" band. The tick marks above the bar code illustration indicate the start of each code. There are 3 + 5 + 3 + 12  * 7 = 95 bands total. In addition there must be at least 9 ``light" bands at either end of the bar code.
\epsfbox{p4880a.eps}
digit L R
pattern pattern
0 0001101 1110010
1 0011001 1100110
2 0010011 1101100
3 0111101 1000010
4 0100011 1011100
5 0110001 1001110
6 0101111 1010000
7 0111011 1000100
8 0110111 1001000
9 0001011 1110100
The last decimal digit in the code is a check sum digit which is computed as follows:

CheckSum = 3*(digit1 + digit3 + digit5 + digit7 + digit9 + digit11) + digit2 + digit4 + digit6 + digit8 + digit10. Code = CheckSum Mod(10); If Code = 0, check digit = 0. Other wise, check digit = (10 - Code).

 

 

 

A bar code scanner could use a camera to take a narrow image across the bar code and deduce the on/off pattern of bands as below:
\epsfbox{p4880b.eps}
if the code was scanned right side up or the following if it was scanned upside down:
\epsfbox{p4880c.eps}
Again, the tick marks above each image indicate the start of each code.
Unfortunately, the images are not always this clear due to lack of contrast or reflections off shiny material, as shown here:
\epsfbox{p4880d.eps}
When scanning the image, it is not always clear whether a particular band is dark or light. It is often still possible to determine the bar code even if we do not know exactly whether a particular band is ``dark" or ``light". First, only 20 of 128 possible 7-bit digit codes are used. Second, only codes with a correct check digit are valid. Finally, even if several codes match, it is unlikely that more than one will be in the database for a particular application. For this problem we will use a ` ?' to indicate uncertainty in the value of a particular band. The start ( S), middle ( M) and end ( E) codes must match in order for a match to be considered valid.
Write a program which takes as input a string of 95 characters, ` 0', ` 1', or ` ?' and outputs all valid UPC-A digit strings which could scan to that sequence of band values in either direction.

Input

The first line of input contains a single integer P, (1$ \le$P$ \le$1000), which is the number of data sets that follow. Each data set consists of 3 lines. The first line contains a single decimal integer which is the problem number (starting at 1). The second line contains the first 50 characters of the input string. The third line contains the final 45 characters of the input string. As noted above, the input string consists of only the characters `0', `1', or `?'.

Output

For each data set there are varying number of lines of output. If no UPC codes match the input string, then the only line of output should contain the problem number (starting at 1), a space, then the digit 0. If more than 8 codes match the input string, the first line of output contains the problem number, a space, then the digit 9. This line is followed by the first 8 codes which match, in ascending numeric order, one per line. Otherwise, the first line of output contains the problem number, a space, then a single decimal digit (1-8) which is the number of matching codes. This line is followed by the matching codes, in ascending numeric order, one per line.

Sample Input

3 
1 
10100110110100111010000100110110001001000010101010 
100011010110001101110110100011000101011000101 
2 
1010001101010001100010????????????1101011000101010 
101000010010001101100100001011100101101100101 
3 
1010001101010001100010????????????1101011000101010 
101000010010001??????????01011100101101100101

Sample Output

1 1 
049705682302 
2 2 
049705682302 
049835682302 
3 9 
049005681302 
049005682002 
049035688302 
049035689002 
049105684302 
049105685002 
049135681302 
049135682002

Problem Source

每周一赛第一场

// Problem#: 2375
// Submission#: 3593627
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
/*
 * H - Scanning UPC Barcodes
 * ACM International Collegiate Programming Contest
 * Greater New York Region
 * October 24, 2010
 */

#include <stdio.h>
#include <math.h>

typedef unsigned char BYTE;
typedef unsigned short WORD;

#define FIRST_SCAN_COUNT    50
#define SECOND_SCAN_COUNT   45
#define MAX_UPCS        12

// bit pattern for left digit codes (LSB is rightmost band
BYTE lpattern[10] = 
{
    0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b
};
// bit pattern for righ digit codes (LSB is rightmost band
BYTE rpattern[10] = 
{
    0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74
};

// digits of found upcs for a given problem instance
char upcvals[MAX_UPCS][12];

// for a given problem instance
// bit 6-i of fwdvals[j] is 1 if the i band of digit code j is a '1' else 0
// bit 6-i of fwdmasks[j] is 1 if the i band of digit code j is not a '?'
// thus if (lpattern[k] & fwdmasks[j]) == fwdvals[j], lpattern[k] matches
// all non-? characters of digit code j so k is a valid digit at location j
BYTE fwdvals[12], fwdmasks[12];
// bit 6-i of backvals[j] is 1 if the i band of digit code j reading in reverse order is a '1' else 0
// bit 6-i of backmasks[j] is 1 if the i band of digit code j reading in reverse order is not a '?'
BYTE backvals[12], backmasks[12];

// bit i of fwddigits[j] is 1 if i is a valid digit for location j in the upc
// bit i of backdigits[j] is 1 if i is a valid digit for location j in the upc
// when read backwards
WORD fwddigits[12], backdigits[12];

// number of codes found for this problem instance so far
int numCodes;

char inbuf[256];

// read data for a single problem instance
// return < 0 for parse errors
// return > 0 if start middle or end code is not valid
// otherwise, fill in fwdvals, fwdmasks, backvals and backmasks
// and return 0
int ParseInData(int probnum)
{
    int i, j, k, ret;
    char c1, c2, c3, c4, c5;
    BYTE frmask, fval, brmask, bval, fmask, bmask;

    ret = 0;
    // get first row of data
    if(fgets(&(inbuf[0]), 255, stdin) == NULL) {
        fprintf(stderr, "Read failed on problem %d first row data\n", probnum);
        return -11;
    }
    // chack format
    for(i = 0; i < 50 ; i++) {
        c1 = inbuf[i];
        if((c1 != '0') && (c1 != '1') && (c1 != '?')) {
            fprintf(stderr, "bad %d char 0x%x on problem %d  first row data\n",
                i, c1, probnum);
            return -12;
        }
    }
    // check start
    c1 = inbuf[0]; c2 = inbuf[1]; c3 = inbuf[2];
    if(((c1 != '1') && (c1 != '?')) || 
        ((c2 != '0') && (c2 != '?')) ||
        ((c3 != '1') && (c3 != '?'))) {
        ret = 1;
    }
    // check middle
    c1 = inbuf[45]; c2 = inbuf[46]; c3 = inbuf[47]; c4 = inbuf[48]; c5 = inbuf[49];
    if(((c1 != '0') && (c1 != '?')) || 
        ((c2 != '1') && (c2 != '?')) ||
        ((c3 != '0') && (c3 != '?')) ||
        ((c4 != '1') && (c4 != '?')) ||
        ((c5 != '0') && (c5 != '?'))) {
        ret = 2;
    }
    // get first 6 digits of data
    for(i = 0, j = 3; i < 6 ; i++, j += 7) {
        frmask = fval = brmask = bval = 0;
        fmask = 0x40;
        bmask = 0x01;
        for(k = 0; k < 7 ; k++) {
            c1 = inbuf[j+k];
            if(c1 == '0') { // valid and blank
                frmask |= fmask;
                brmask |= bmask;
            } else if(c1 == '1') { // valid and bar
                frmask |= fmask;
                brmask |= bmask;
                fval |= fmask;
                bval |= bmask;
            }   // else unknown char
            fmask >>= 1;
            bmask <<= 1;
        }
        fwdvals[i] = fval;
        fwdmasks[i] = frmask;
        backvals[11 - i] = bval;
        backmasks[11 - i] = brmask;
    }
    // get second row of data
    if(fgets(&(inbuf[0]), 255, stdin) == NULL) {
        fprintf(stderr, "Read failed on problem %d second row data\n", probnum);
        return -15;
    }
    // check format
    for(i = 0; i < 45 ; i++) {
        c1 = inbuf[i];
        if((c1 != '0') && (c1 != '1') && (c1 != '?')) {
            fprintf(stderr, "bad %d char 0x%x on problem %d  second row data\n",
                i, c1, probnum);
            return -16;
        }
    }
    // check end
    c1 = inbuf[42]; c2 = inbuf[43]; c3 = inbuf[44];
    if(((c1 != '1') && (c1 != '?')) || 
        ((c2 != '0') && (c2 != '?')) ||
        ((c3 != '1') && (c3 != '?'))) {
        fprintf(stderr, "bad end code %c%c%c on problem %d  data\n",
            c1, c2, c3, probnum);
        ret = 3;
    }
    // get second 6 digits of data
    for(i = 0, j = 0; i < 6 ; i++, j += 7) {
        frmask = fval = brmask = bval = 0;
        fmask = 0x40;
        bmask = 0x01;
        for(k = 0; k < 7 ; k++) {
            c1 = inbuf[j+k];
            if(c1 == '0') { // valid and blank
                frmask |= fmask;
                brmask |= bmask;
            } else if(c1 == '1') { // valid and bar
                frmask |= fmask;
                brmask |= bmask;
                fval |= fmask;
                bval |= bmask;
            }   // else unknown
            fmask >>= 1;
            bmask <<= 1;
        }
        fwdvals[6+i] = fval;
        fwdmasks[6+i] = frmask;
        backvals[5 - i] = bval;
        backmasks[5 - i] = brmask;
    }
    return ret;
}

// for a single problem instance
// using fwdvals, fwdmasks, backvals and backmasks computed in ParseInData,
// compute fwddigits and back digits
void GetDigits()
{
    int i, j;
    WORD outmask;
    for(i = 0; i < 12; i++) {
        fwddigits[i] = backdigits[i] = 0;
    }
    // left digits
    for(i = 0; i < 6; i++) {
        outmask = 1;
        for(j = 0; j < 10 ; j++) {
            if((lpattern[j] & fwdmasks[i]) == fwdvals[i]) {
                // j is a valid digit for location i
                fwddigits[i] |= outmask;
            }
            if((lpattern[j] & backmasks[i]) == backvals[i]) {
                // j is a valid digit for location i (reversed)
                backdigits[i] |= outmask;
            }
            outmask <<= 1;
        }
    }
    // right digits
    for(i = 6; i < 12; i++) {
        outmask = 1;
        for(j = 0; j < 10 ; j++) {
            if((rpattern[j] & fwdmasks[i]) == fwdvals[i]) {
                // j is a valid digit for location i
                fwddigits[i] |= outmask;
            }
            if((rpattern[j] & backmasks[i]) == backvals[i]) {
                // j is a valid digit for location i (reversed)
                backdigits[i] |= outmask;
            }
            outmask <<= 1;
        }
    }
}

// compute check digit value for first 11 digits in upcDigits
char CompCheckDigit(char *upcDigits)
{
    int sum;
    sum = upcDigits[0] + upcDigits[2] + upcDigits[4] +
            upcDigits[6] + upcDigits[8] + upcDigits[10];
    sum = sum * 3 + upcDigits[1] + upcDigits[3] +
            upcDigits[5] + upcDigits[7] + upcDigits[9];
    sum = sum % 10;
    if(sum == 0) return 0;
    else return (char)(10 - sum);
}

// recursive check of valid upc codes
// index is which digit we are processing
// upc is the current upc code to this point
// fwdValid != 0 if we got here via fwdDigits
// backValid != 0 if we got here via backDigits
// NOTE that since we test upc codes in nnumerical order
// they will be added to the upcvals array in numeric order
void CheckValidCodes(int index, char *upc, char fwdValid, char backValid)
{
    char digit, newfwd, newback;
    int i;
    WORD mask;
    // check too many codes
    if(numCodes >= MAX_UPCS) {
        return;
    }
    // end case
    if(index == 11) {   // check digit
        digit = CompCheckDigit(upc);
        mask = (1 << digit);
        if((fwdValid && ((mask & fwddigits[11]) != 0)) ||
            (backValid && ((mask & backdigits[11]) != 0))) {
            // if the check digit is valid for the last location in either
            // direction and all previous digits are OK, it is a match, save it
            upc[11] = digit;
            for(i = 0; i < 12 ; i++) {
                upcvals[numCodes][i] = '0' + upc[i];
            }
            numCodes++;
        }
    }
    else { // scan through all digit possibilities
        for(digit = 0, mask = 1; digit < 10 ; digit ++, mask <<= 1) {
            newfwd = newback = 0;
            if(fwdValid && ((mask & fwddigits[index]) != 0)) newfwd = 1;
            if(backValid && ((mask & backdigits[index]) != 0)) newback = 1;
            // if this digit (and all previous digits match either forward or back
            // save the current digit and make a recursive call for the next digit
            if(newfwd || newback) {
                upc[index] = digit;
                CheckValidCodes(index+1, upc, newfwd, newback);
            }
        }
        // check too many codes
        if(numCodes >= MAX_UPCS) {
            return;
        }
    }
}

void PrintSoln(int probnum)
{
    int i;
    if(numCodes == 0) {
        printf("%d 0\n", probnum);  // no codes to print
        return;
    } else if(numCodes > 8) {
        printf("%d 9\n", probnum, numCodes);
        // only print the first 8
        numCodes = 8;
    } else {
        printf("%d %d\n", probnum, numCodes);
    }
    for(i = 0; i < numCodes ; i++) {
        printf("%c%c%c%c%c%c%c%c%c%c%c%c\n", 
            upcvals[i][0], upcvals[i][1], upcvals[i][2],
            upcvals[i][3], upcvals[i][4], upcvals[i][5],
            upcvals[i][6], upcvals[i][7], upcvals[i][8],
            upcvals[i][9], upcvals[i][10], upcvals[i][11]);
    }
}

// if start, middle and end match, find all upc codes that match
// fwdvals, fwdmasks, backvals and backmasks already computed in ParseInData
int FindUPCs(int probnum)
{
    char curUpc[12];
    GetDigits();    // get fwddigits and backdigits
    numCodes = 0;   // init count to 0
    CheckValidCodes(0, &(curUpc[0]), 1, 1); // recursively find (at most MAX_UPCS) matching upc codes
    PrintSoln(probnum); // print the ones we found
    return 0;
}

int main()
{
    int nprob, curprob, index, ret;

    if(fgets(&(inbuf[0]), 255, stdin) == NULL)
    {
        fprintf(stderr, "Read failed on problem count\n");
        return -1;
    }
    if(sscanf(&(inbuf[0]), "%d", &nprob) != 1)
    {
        fprintf(stderr, "Scan failed on problem count\n");
        return -2;
    }
    for(curprob = 1; curprob <= nprob ; curprob++)
    {
        if(fgets(&(inbuf[0]), 255, stdin) == NULL)
        {
            fprintf(stderr, "Read failed on problem %d data\n", curprob);
            return -3;
        }
        if(sscanf(&(inbuf[0]), "%d", &index) != 1)
        {
            fprintf(stderr, "Scan failed on problem %d data\n", curprob);
            return -4;
        }
        if(index != curprob)
        {
            fprintf(stderr, "problem index %d not = expected problem %d\n",
                index, curprob);
            return -7;
        }
        ret = ParseInData(curprob);
        if(ret > 0) {   // start middle or end did not match so no solutions
            printf("%d 0\n", curprob);
        } else if(ret == 0) {   // data to process
            FindUPCs(curprob);
        } else {    // bad input format (should not happen)
            return ret;
        }
    }
    return 0;
}                                 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值