您的位置:首页 > 大数据 > 人工智能

usaco training 4.3.2 The Primes 题解

2014-02-27 15:32 656 查看
The Primes题解

IOI'94


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



描述

在下面的方格中,每行,每列,以及两条对角线上的数字可以看作是五位的素数。方格中的行按照从左到右的顺序组成一个素数,而列按照从上到下的顺序。两条对角线也是按照从左到右的顺序来组成。
+---+---+---+---+---+
| 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 |
+---+---+---+---+---+

这些素数各个数位上的和必须相等。
左上角的数字是预先定好的。
一个素数可能在方阵中重复多次。
如果不只有一个解,将它们全部输出(按照这25个数字组成的25位数的大小排序)。
一个五位的素数开头不能为0(例如:00003 不是五位素数)


[编辑]格式

PROGRAM NAME: prime3

INPUT FORMAT:

(file prime3.in)

一行包括两个被空格分开的整数:各数位上的数字的和 以及左上角的数字。

OUTPUT FORMAT:

(file prime3.out)

对于每一个找到的方案输出5行,每行5个字符, 每行可以转化为一个5位的质数.在两组方案中间输出一个空行. 如果没有解就单独输出一行"NONE"。


[编辑]SAMPLE
INPUT

11 1


[编辑]SAMPLE
OUTPUT

上面的例子有三组解。
11351
14033
30323
53201
13313

11351
33203
30323
14033
33311

13313
13043
32303
50231
13331


序言:总算AC了。我花了一个半小时编完了程序,又花了两个小时调试程序,眼睛都花了。尽管在种种挫折前,我屡次萌生“放弃此题,随便贴个代码”的想法,但最终还是挺过去了。

这种方阵题很是多见。原来我都是直接暴力地顺序枚举(准确地说是有顺序枚举)。在这个题目面前,效率显然是非常低的。经过很长时间的探索,我总结出了一张枚举顺序的图。

 
                                           如图,黄色的代表不能为0,绿色的代表必须是1,3,5,7,9(这些是结尾)。

枚举的顺序要遵循3个原则:①控制位置多的先来,以便剪枝。②相对有些规律,不要杂乱无章。③有些数字可以通过计算出来,无需枚举。(即图中的粉红数字,共12个)

但是问题很快就出来了:这样不就不能有规律地递归了吗?

为了不TLE,我毅然打起了13(25-12)个循环。

刚开始的时候,我开了xie1-xie2,hang1-hang5,lie1-lie5这些变量来分别记录各块的数字和。(以下为某处代码)

for (i2=1;i2<=5;i2++)
{
a[5][1]=mei[i2];lie1=a[1][1]+a[5][1];hang5=a[5][1]+a[5][5];if (hang5>sum||lie1>sum) break;


这个很实用。举个例子,xie1代表从左上到右下的(部分)数字和。这样,我不仅第6次枚举时把第8块的区域值算出来,还可以在之前的某处来个剪枝(比如,枚举到第5块方格时,若xie1=a[1][1]+a[5][5]+a[3][3]>sum(sum为给定的各块的数字和)时,直接跳掉)。我们还能看出这个剪枝很有效——由样例输出可知,大的个位数字几乎不曾出现。

然而这是有问题的。假设xie1累加后在某处直接break掉,它的值是不会改变的。下一次枚举另外一个数字时,xie1会继续加上。要避免问题,不能在循环里改变值,而要在函数里带入形参。很明显,这是不现实的。

就没有解决方案了吗?我想了一会,改了一些地方。如刚才的代码:

for (i1=1;i1<=5;i1++)
{
a[5][5]=mei[i1];xie1=a[1][1]+a[5][5];if (xie1>sum) {xie1=0;break;}
for (i2=1;i2<=5;i2++)
{
a[5][1]=mei[i2];lie1=a[1][1]+a[5][1];hang5=a[5][1]+a[5][5];if (hang5>sum||lie1>sum) {hang5=0;lie1=0;break;}


这好像递归中的回溯操作。

但是问题又来了:如果当时i1的值是5,而且到i2时被break掉了,那么xie1的值不能被减掉!

思考再三,我只好采用直接赋值的方法,放弃每次+=的优化方法,也减掉了一些剪枝。

还好,我最终成功了!

代码:

/*
PROG:prime3
ID:juan1973
LANG:C++
*/
#include<stdio.h>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=500+5;
int mei[6]={0,1,3,5,7,9};
int a[6][6],i1,i2,i3,jj,j2,j3,j4,k1,k2,p1,p2,p3,p4,p5,p6,sum,p,up,cnt,i,j,k,min,ans[maxn][6][6],num[maxn];
int hang1,hang2,hang3,hang4,hang5,lie1,lie2,lie3,lie4,lie5,xie1,xie2;
bool f[10][10][10][10][10],flag[maxn];
void first()                                        //预处理10000-99999之间的质数
{
int w1,w2,w3,w4,w5,s;
memset(f,1,sizeof(f));
for (int i=2;i<=trunc(sqrt(99999));i++)
for (int j=10000/i;j<=99999/i;j++)
{
s=i*j;w5=s%10;s=s/10;
w4=s%10;s=s/10;
w3=s%10;s=s/10;
w2=s%10;s=s/10;
f[s][w2][w3][w4][w5]=false;
}
}
bool xiao(int p,int q)                              //比大小,用于排序
{
for (int i=1;i<=5;i++)
for (int j=1;j<=5;j++)
if (ans[p][i][j]<ans[q][i][j]) return true;
else if (ans[p][i][j]>ans[q][i][j]) return false;
}
int main()
{
freopen("prime3.in","r",stdin);
freopen("prime3.out","w",stdout);
scanf("%ld%ld",&sum,&p);
a[1][1]=p;up=9;if (sum<up) up=sum;
first();
for (i1=1;i1<=5;i1++)
{
a[5][5]=mei[i1];
for (i2=1;i2<=5;i2++)
{
a[5][1]=mei[i2];
for (i3=1;i3<=5;i3++)
{
a[1][5]=mei[i3];
for (p1=0;p1<=up;p1++)
{
a[3][3]=p1;
if (a[1][1]+a[5][5]+a[3][3]>sum||a[5][1]+a[1][5]+a[3][3]>sum) continue;
for (p2=0;p2<=up;p2++)
{
a[4][4]=p2;
if (a[5][5]==1&&a[5][1]==1&&a[1][5]==3&&a[3][3]==3&&a[4][4]==3)
a[3][3]=3;
xie1=a[1][1]+a[5][5]+a[3][3]+a[4][4];if (xie1>sum) continue;
a[2][2]=sum-xie1;
/*1-------*/if (a[2][2]<0||a[2][2]>9||!f[a[1][1]][a[2][2]][a[3][3]][a[4][4]][a[5][5]]) continue;
for (p3=0;p3<=up;p3++)
{
a[4][2]=p3;
if (a[5][5]==1&&a[5][1]==1&&a[1][5]==3&&a[3][3]==3&&a[4][4]==3&&a[4][2]==0)
a[3][3]=3;
xie2=a[5][1]+a[1][5]+a[3][3]+a[4][2];if (xie2>sum) continue;
/*2-------*/  a[2][4]=sum-xie2;if (a[2][4]<0||a[2][4]>9||!f[a[5][1]][a[4][2]][a[3][3]][a[2][4]][a[1][5]]) continue;
for (jj=1;jj<=5;jj++)
{
a[5][2]=mei[jj];if (a[5][1]+a[5][5]+a[5][2]>sum||a[2][2]+a[4][2]+a[5][2]>sum) continue;
if (a[5][5]==1&&a[5][1]==1&&a[1][5]==3&&a[3][3]==3&&a[4][4]==3&&a[4][2]==0&&a[5][2]==3)
a[3][3]=3;
for (j2=1;j2<=5;j2++)
{
a[5][3]=mei[j2];hang5=a[5][1]+a[5][5]+a[5][2]+a[5][3];if (hang5>sum) continue;
/*3-------*/    a[5][4]=sum-hang5;if (a[5][4]<0||a[5][4]>9||!f[a[5][1]][a[5][2]][a[5][3]][a[5][4]][a[5][5]]) continue;
if (a[2][4]+a[4][4]+a[5][4]>sum) continue;
for (j3=1;j3<=5;j3++)
{
a[2][5]=mei[j3];
if (a[1][5]+a[2][5]+a[5][5]>sum||a[2][2]+a[2][4]+a[2][5]>sum) continue;
for (j4=1;j4<=5;j4++)
{
a[3][5]=mei[j4];lie5=a[1][5]+a[2][5]+a[5][5]+a[3][5];a[4][5]=sum-lie5;
/*4-------*/        if (a[4][5]<0||a[4][5]>9||!f[a[1][5]][a[2][5]][a[3][5]][a[4][5]][a[5][5]]) continue;
if (a[4][2]+a[4][4]+a[4][5]>sum) continue;
for (p4=1;p4<=up;p4++)
{
a[1][2]=p4;
lie2=a[2][2]+a[4][2]+a[5][2]+a[1][2];if (lie2>sum||a[1][1]+a[1][5]+a[1][2]>sum) continue;
/*5-------*/          a[3][2]=sum-lie2;if (a[3][2]<0||a[3][2]>9||!f[a[1][2]][a[2][2]][a[3][2]][a[4][2]][a[5][2]]) continue;
if (a[3][2]+a[3][3]+a[3][5]>sum) continue;
for (p5=1;p5<=up;p5++)
{
a[1][3]=p5;
hang1=a[1][1]+a[1][5]+a[1][2]+a[1][3];if (hang1>sum||a[1][3]+a[3][3]+a[5][3]>sum)continue;
/*6-------*/            a[1][4]=sum-hang1;if (a[1][4]<1||a[1][4]>9||!f[a[1][1]][a[1][2]][a[1][3]][a[1][4]][a[1][5]]) continue;
lie4=a[2][4]+a[4][4]+a[5][4]+a[1][4];
/*7-------*/            a[3][4]=sum-lie4;if (a[3][4]<0||a[3][4]>9||!f[a[1][4]][a[2][4]][a[3][4]][a[4][4]][a[5][4]]) continue;
hang3=a[3][2]+a[3][3]+a[3][5]+a[3][4];if (hang3>sum) continue;
/*8-------*/            a[3][1]=sum-hang3;if (a[3][1]<1||a[3][1]>9||!f[a[3][1]][a[3][2]][a[3][3]][a[3][4]][a[3][5]]) continue;
if (a[1][1]+a[3][1]+a[5][1]>sum) continue;
for (p6=1;p6<=up;p6++)
{
a[2][1]=p6;
hang2=a[2][2]+a[2][4]+a[2][5]+a[2][1];lie1=a[1][1]+a[3][1]+a[5][1]+a[2][1];if (hang2>sum||lie1>sum) continue;
/*9-------*/             a[2][3]=sum-hang2;if (a[2][3]<0||a[2][3]>9||!f[a[2][1]][a[2][2]][a[2][3]][a[2][4]][a[2][5]]) continue;
/*10------*/             a[4][1]=sum-lie1;if (a[4][1]<1||a[4][1]>9||!f[a[1][1]][a[2][1]][a[3][1]][a[4][1]][a[5][1]]) continue;
lie3=a[1][3]+a[3][3]+a[5][3]+a[2][3];hang4=a[4][2]+a[4][4]+a[4][5]+a[4][1];if (lie3>sum||hang4>sum) continue;
if (lie3!=hang4||sum-hang4<0||sum-hang4>9) continue;
a[4][3]=sum-hang4;
/*11-----*//*12----*/if (!f[a[1][3]][a[2][3]][a[3][3]][a[4][3]][a[5][3]]||!f[a[4][1]][a[4][2]][a[4][3]][a[4][4]][a[4][5]]) continue;
cnt++;
for (k1=1;k1<=5;k1++)
for (k2=1;k2<=5;k2++)
ans[cnt][k1][k2]=a[k1][k2];
}
}
}
}
}
}
}
}
}
}
}
}
}
memset(flag,0,sizeof(flag));
for (i=1;i<=cnt;i++)                                //用n^2的标号法排序,避免交换。
{
for (j=1;j<=cnt;j++)
if (!flag[j]) break;
min=j;
for (k=j+1;k<=cnt;k++)
if (!flag[k]&&xiao(k,min)) min=k;
flag[min]=true;num[min]=i;
}
if (cnt==0) printf("NONE");
else
{
for (i=1;i<=cnt;i++)
{
for (j=1;j<=cnt;j++)
if (num[j]==i) break;
for (i2=1;i2<=5;i2++)
{
for (i3=1;i3<=5;i3++)
printf("%ld",ans[j][i2][i3]);
printf("\n");
}
if (i<cnt) printf("\n");
}
}
return 0;
}



















                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  usaco training 枚举