usaco 4.3.2 primes

本文介绍了一个关于寻找特殊五位数质数矩阵的问题解决方法。该矩阵要求每一行、每一列及两条对角线组成的数字均为质数,并且所有数字的各位数之和相等。文章详细解释了如何通过深度优先搜索(DFS)策略来寻找所有可能的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

In the square below, each row, each column and the two diagonals can be read as a five digit prime number. The rows are read from left to right. The columns are read from top to bottom. Both diagonals are read from left to right.

+---+---+---+---+---+
| 1 | 1 | 3 | 5 | 1 |
+---+---+---+---+---+
| 3 | 3 | 2 | 0 | 3 |
+---+---+---+---+---+
| 3 | 0 | 3 | 2 | 3 |
+---+---+---+---+---+
| 1 | 4 | 0 | 3 | 3 |
+---+---+---+---+---+
| 3 | 3 | 3 | 1 | 1 |
+---+---+---+---+---+ 
  • The prime numbers' digits must sum to the same number.
  • The digit in the top left-hand corner of the square is pre-determined (1 in the example).
  • A prime number may be used more than once in the same square.
  • If there are several solutions, all must be presented (sorted in numerical order as if the 25 digits were all one long number).
  • A five digit prime number cannot begin with a zero (e.g., 00003 is NOT a five digit prime number).

PROGRAM NAME: prime3

INPUT FORMAT

A single line with two space-separated integers: the sum of the digits and the digit in the upper left hand corner of the square.

SAMPLE INPUT (file prime3.in)

11 1

OUTPUT FORMAT

Five lines of five characters each for each solution found, where each line in turn consists of a five digit prime number. Print a blank line between solutions. If there are no prime squares for the input data, output a single line containing "NONE".

SAMPLE OUTPUT (file prime3.out)

The above example has 3 solutions.

11351
14033
30323
53201
13313

11351
33203
30323
14033
33311

13313
13043
32303
50231
13331

题目分析:
使用dfs搜索。看似简单,写起来非常恶心。目测是我在usaco上代码量最长的程序。
枚举的时候注意顺序。
(1)先枚举第一行和第一列(因为左上角的数字已经给出,因此只需要枚举4个数字),下图中标“1”的地方表示已经枚举出来。
1 1 1 1 1
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
(2)再枚举副对角线:(第一个数字和最后一个数字已知,只需要枚举3个数字)。
1 1 1 1 1
1 0 0 1 0
1 0 1 0 0
1 1 0 0 0
1 0 0 0 0
(3)枚举主对角线:(第一个数字和第三个数字已知,只需要枚举3个数字):
1 1 1 1 1
1 1 0 1 0
1 0 1 0 0
1 1 0 1 0
1 0 0 0 1
(4)枚举第三行:(第一个数字和第三个数字已知,只需要枚举3个数字):
1 1 1 1 1
1 1 0 1 0
1 1 1 1 1
1 1 0 1 0
1 0 0 0 1
(5)枚举第三列:(第一个数字和第三个数字已知,只需要枚举3个数字):
1 1 1 1 1
1 1 1 1 0
1 1 1 1 1
1 1 1 1 0
1 0 11
(6)经过(1)~(5),可以发现,剩下的四个空的位置,已经可以推出(每一行每一列的和都是定值sum)。因此dfs到第六层的时候,这个矩阵就确定下来了。这个时候对其进行check,看是否满足题意(每行每列+对角线都是质数,每行每列+对角线的和都是定值sum)。


值得注意的是,我们可以发现(3)(4)(5)步骤非常神似,因此我们可以预处理一下,将那种 已知第1个和第3个数字,满足题设条件的剩余的三个数字的所有情况都存入到链表中(在我的代码中是thirdlist),同理第(1)步骤的情况存入firstlist,第(2)步骤的情况存入secondlist。
此外,还需要加一个剪枝操作:事实上进行完第(4)步骤后,就可以求得矩阵中(5,2)和(5,4)这两个位置的值,可以先进行一个check,如果不满足,则不继续进行第(5)步。
加上这个减枝后,最大样例可以在0.2秒通过。
分析
/*
ID: lisendo1
PROG: prime3
LANG: C++
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXN 200
#define nil -1
#define oo 100000000.0
using namespace std;
ifstream in("prime3.in"); 
ofstream out("prime3.out");
bool primeflag[100010] = {0};
int one, sum;
bool isprime(int num) {
    for (int i = 2; i <= sqrt(num); i++) {
        if (num % i == 0) return false;
    }
    return true;
}
struct first{
    int  b, c, d, e;
    struct first * next;
};
first * firstlist = NULL;
struct second{
    int b, c, d;
    struct second * next;
};
second * secondlist[20][20] = {NULL};
struct third {
    int b, d, e;
    struct third * next;
};
third * thirdlist[20][20] = {NULL};
//struct forth {
//    int a, c, e;
//    struct forth * next;
//};
//forst * forthlist[20][20] = {NULL};
struct result {
    int data[6][6];
};
int r[6][6];
result total_r[2000];
int result_num = 0;
bool check1(int num) {
    int tmpnum = num;
    int tail = num % 10;
    //if (tail != 1 && tail != 3 && tail != 7 && tail != 9) return false;
    while (num) {
        tail = num % 10;
        num /= 10;
        if (tail == 0) return false;
    }
    int tmpsum = 0;
    num = tmpnum;
    while (num) {
        tmpsum += num % 10;
        num /= 10;
    }
    if (tmpsum != sum) return false;
    return true;
}
bool check2(int i, int mid, int j) {
    int tmpsum = i + j;
    while (mid) {
        tmpsum += mid % 10;
        mid /= 10;
    }
    if (tmpsum == sum) return true;
    else return false;
}
void init() {
    for (int num = 10000; num <= 99999; num++) {
        if (isprime(num)) primeflag[num] = true;
    }
    cout << "prime flag finish!" << endl;
    for (int num = one * 10000; num <= one * 10000 + 9999; num++) {
        if (primeflag[num] && check1(num)) {
            first * tmp1 = (first*)malloc(sizeof(first));
            tmp1->b = num / 1000 % 10;
            tmp1->c = num / 100 % 10;
            tmp1->d = num / 10 % 10;
            tmp1->e = num % 10;
            tmp1->next = firstlist;
            firstlist = tmp1;
        }
    }
    cout << "list 1 finish!" << endl;
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= 9; j++) {
            for (int mid = 0; mid<= 999; mid++) {
                int num = mid * 10 + j + i * 10000;
                if (primeflag[num] && check2(i, mid, j)) {
                    second * tmp2 = (second*)malloc(sizeof(second));
                    tmp2->b = mid / 100 % 10;
                    tmp2->c = mid / 10 % 10;
                    tmp2->d = mid % 10;
                    tmp2->next = secondlist[i][j];
                    secondlist[i][j] = tmp2;
                }
            }
        }
    }
    cout << "list 2 finish!" << endl;
    for (int i = 1; i <= 9; i++) {
        for (int j = 0; j <= 9; j++) {
            for (int mid = 0; mid <= 999; mid++) {
                    int num = i * 10000;
                    num += (mid / 100 % 10) * 1000;
                    num += j * 100;
                    num += (mid / 10 % 10) * 10;
                    num += (mid % 10);
                    if (primeflag[num] && check2(i, mid, j)) {
                        third * tmp3 = (third*) malloc (sizeof(third));
                        tmp3->b = mid / 100 % 10;
                        tmp3->d = mid / 10 % 10;
                        tmp3->e = mid % 10;
                        tmp3->next = thirdlist[i][j];
                        thirdlist[i][j] = tmp3;
                    }
            }
        }
    }
    cout << "list 3 finish!" << endl;
    //for (int i = 0; i <= 9; i++) {
    //    for (int j = 0; j <= 9; j++) {
    //        for(int mid = 100; mid <= 999; mid ++) {
    //            int num = (mid / 100) * 10000;
    //            num += i * 1000;
    //            num += (mid / 10 % 10) * 100;
    //            num += j * 10;
    //            num += (mid % 10);
    //            if (primeflag(num)) {
    //                forth * tmp4 = (forth*) malloc(sizeof(forth));
    //                tmp4->a = mid / 100;
    //                tmp4->c = mid / 10 % 10;
    //                tmp4->e = mid % 10;
    //                tmp4->next = forthlist[i][j];
    //                forthlist[i][j] = tmp4;
    //            }
    //        }
    //    }
    //}
}
bool check_result() {
    r[2][5] = sum - r[2][1] - r[2][2] - r[2][3] - r[2][4];
    if (r[2][5] < 0 || r[2][5] > 9) return false;
    int num1 = r[2][1] * 10000 + r[2][2] * 1000 + r[2][3] * 100 + r[2][4] * 10 + r[2][5];
    if (!primeflag[num1]) return false;
    
    r[4][5] = sum - r[4][1] - r[4][2] - r[4][3] - r[4][4];
    if (r[4][5] < 0 || r[4][5] > 9) return false;
    num1 = r[4][1] * 10000 + r[4][2] * 1000 + r[4][3] * 100 + r[4][4] * 10 + r[4][5];
    if (!primeflag[num1]) return false;

    //r[5][2] = sum - r[1][2] - r[2][2] - r[3][2] - r[4][2];
    //if (r[5][2] < 0 || r[5][2] > 9) return false;
    //num1 = r[1][2] * 10000 + r[2][2] * 1000 + r[3][2] * 100 + r[4][2] * 10 + r[5][2];
    //if (!primeflag[num1]) return false;

    //r[5][4] = sum - r[1][4] - r[2][4] - r[3][4] - r[4][4];
    //if (r[5][4] < 0 || r[5][4] > 9) return false;
    //num1 = r[1][4] * 10000 + r[2][4] * 1000 + r[3][4] * 100 + r[4][4] * 10 + r[5][4];
    //if (!primeflag[num1]) return false;

    if (sum != (r[5][1] + r[5][2] + r[5][3] + r[5][4] + r[5][5])) return false;
    if (sum != (r[1][5] + r[2][5] + r[3][5] + r[4][5] + r[5][5])) return false;

    num1 = r[1][5] * 10000 + r[2][5] * 1000 + r[3][5] * 100 + r[4][5] * 10 + r[5][5];
    if (!primeflag[num1]) return false;

    num1 = r[5][1] * 10000 + r[5][2] * 1000 + r[5][3] * 100 + r[5][4] * 10 + r[5][5];
    if (!primeflag[num1]) return false;
    //int i, j;
    //bool flag[10] = {0};
    //for (i = 1; i <= 5; i++) {
    //    for (j = 1; j <= 5; j++) {
    //        if (r[i][j] != 0 && r[i][j] != 1 && r[i][j] != 2 && r[i][j] != 3 && r[i][j] != 5 && r[i][j] != 7) {
    //            if (flag[r[i][j]] != 0) return false;
    //            flag[r[i][j]]++;
    //        }
    //    }
    //}
    return true;
}
void dfs6(void) {
    third * cur;
    for (cur = thirdlist[r[1][3]][r[3][3]]; cur != NULL; cur = cur->next) {
        r[2][3] = cur->b; r[4][3] = cur->d; r[5][3] = cur->e;
        if (check_result()) {
            memcpy(total_r[result_num].data, r, sizeof(r));
            result_num++;
        }
    }
}
void dfs5(void) {
    third * cur;
    for (cur = thirdlist[r[3][1]][r[3][3]]; cur != NULL; cur = cur->next) {
        r[3][2] = cur->b; r[3][4] = cur->d; r[3][5] = cur->e;
        //delete branch
        r[5][2] = sum - r[1][2] - r[2][2] - r[3][2] - r[4][2];
        if (r[5][2] < 0 || r[5][2] > 9) continue;
        int num1 = r[1][2] * 10000 + r[2][2] * 1000 + r[3][2] * 100 + r[4][2] * 10 + r[5][2];
        if (!primeflag[num1]) continue;

        r[5][4] = sum - r[1][4] - r[2][4] - r[3][4] - r[4][4];
        if (r[5][4] < 0 || r[5][4] > 9) continue;
        num1 = r[1][4] * 10000 + r[2][4] * 1000 + r[3][4] * 100 + r[4][4] * 10 + r[5][4];
        if (!primeflag[num1]) continue;

        //if (sum != (r[5][1] + r[5][2] + r[5][3] + r[5][4] + r[5][5])) continue;

        //num1 = r[5][1] * 10000 + r[5][2] * 1000 + r[5][3] * 100 + r[5][4] * 10 + r[5][5];
        //if (!primeflag[num1]) continue;
        dfs6();
    }
}
void dfs4(void) {
    third * cur;
    for (cur = thirdlist[r[1][1]][r[3][3]]; cur != NULL; cur = cur->next) {
        r[2][2] = cur->b; r[4][4] = cur->d; r[5][5] = cur->e;
        dfs5();
    }
}
void dfs3(void) {
    second * cur;
    for (cur = secondlist[r[5][1]][r[1][5]]; cur != NULL; cur = cur->next) {
        r[4][2] = cur->b; r[3][3] = cur->c; r[2][4] = cur->d;
        dfs4();
    }
}
void dfs2(void) {
    first * cur;
    for (cur = firstlist; cur != NULL; cur = cur->next) {
        r[2][1] = cur->b; r[3][1] = cur -> c; r[4][1] = cur->d; r[5][1] = cur->e;
        dfs3();
    }
}
void dfs1(void) {
    first * cur;
    for (cur = firstlist; cur != NULL; cur = cur->next) {
        r[1][1] = one;
        r[1][2] = cur->b; r[1][3] = cur->c; r[1][4] = cur->d; r[1][5] = cur->e;
        dfs2();
    }
}
int cmp(result a, result b) {
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= 5; j++) {
            if (a.data[i][j] < b.data[i][j]) return 1;
            else if (a.data[i][j] > b.data[i][j]) return 0;
        }
    }
}
int main()
{
    in >> sum >> one;
    init();
    cout << "init finish!" << endl;
    dfs1();
    sort(total_r, total_r + result_num, cmp);
    cout << "The above example has " << result_num <<" solutions.\n\n";
    //printf("The above example has %d solutions.\n\n", result_num);
    for (int i = 0; i < result_num; i++) {
        for (int j = 1; j <= 5; j++) {
            for (int k = 1; k <= 5; k++) {
                out << total_r[i].data[j][k];
//                printf("%d", total_r[i].data[j][k]);
            }
                out << endl;
 //           printf("\n");
        }
        if (i != result_num - 1)
            out << endl;
//        printf("\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值