usaco4.3.2

先贴代码
USER: Gao Bicheng [volz.kz1]
TASK: prime3
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.011 secs, 8032 KB]
   Test 2: TEST OK [0.011 secs, 8032 KB]
   Test 3: TEST OK [0.032 secs, 8032 KB]
   Test 4: TEST OK [0.032 secs, 8032 KB]
   Test 5: TEST OK [0.043 secs, 8032 KB]
   Test 6: TEST OK [0.022 secs, 8032 KB]
   Test 7: TEST OK [0.032 secs, 8032 KB]
   Test 8: TEST OK [0.043 secs, 8032 KB]
   Test 9: TEST OK [0.065 secs, 8032 KB]
   Test 10: TEST OK [0.054 secs, 8032 KB]


All tests OK.

/*
ID: volz.kz.g
PROB: prime3
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("prime3.in");
ofstream fout("prime3.out");

int sum,tot;//sum表示每一行每一列以及对角线的数字之和,tot保存大于10000的素数个数
int seq[25];//保存25位长度的序列
int prime[20000];//保存大于10000的第i个素数
bool check[100000];//判断编号为i的数字是否为素数
int put_prime_first[46][10][200];
int put_prime_third[46][10][200];
int put_prime_second_fourth[46][10][10][20];
int put_prime_first_third_fifth[46][10][10][10][10];
int put_prime_second_third_fourth[46][10][10][10][10];
int ans[100][25];
int ans_num;
inline void euler(){
  memset(check,true,sizeof(check));
  for (int i=2;i<100000;i++){
    if (check[i]) prime[tot++]=i;
    for (int j=0;j<tot;j++){
      if (i*prime[j]>=100000) break;
      check[i*prime[j]]=false;
      if (i%prime[j]==0) break;
    }
  }
}
inline void get_put_prime(){
  for (int i=0;i<tot;i++)
    if (prime[i]>=10000){
      int j=prime[i],k=0,k1=prime[i]/10000,k2=(prime[i]/1000)%10,k3=(prime[i]/100)%10,k4=(prime[i]/10)%10,k5=(prime[i])%10;
      while (j>0){
    k+=j % 10;
    j/=10;
      }
      put_prime_first[k][k1][0]++;
      put_prime_first[k][k1][put_prime_first[k][k1][0]]=prime[i];
      put_prime_third[k][k3][0]++;
      put_prime_third[k][k3][put_prime_third[k][k3][0]]=prime[i];
      put_prime_second_fourth[k][k2][k4][0]++;
      put_prime_second_fourth[k][k2][k4][put_prime_second_fourth[k][k2][k4][0]]=prime[i];
      put_prime_first_third_fifth[k][k1][k3][k5][0]++;
      put_prime_first_third_fifth[k][k1][k3][k5][put_prime_first_third_fifth[k][k1][k3][k5][0]]=prime[i];
      put_prime_second_third_fourth[k][k2][k3][k4][0]++;
      put_prime_second_third_fourth[k][k2][k3][k4][put_prime_second_third_fourth[k][k2][k3][k4][0]]=prime[i];
    }
}
inline bool check_func(int x,int y){
  for (int i=0;i<25;i++){
    if (ans[x][i]<ans[y][i]) return false;
    if (ans[x][i]>ans[y][i]) return true;
  }
  return false;
}
inline void print(){
  for (int j=1;j<=ans_num;j++){
    for (int i=0;i<25;i++){
      fout << ans[j][i];
      if ((i+1)%5==0) fout << endl;
    }
    if (j!=ans_num) fout << endl;
  }
}
inline void find_seq(int step){
  switch (step){
    int k1,k2,k3,k4,k5,num;
  case 0:
    k1=seq[0];
    for (int i=1;i<=put_prime_first[sum][k1][0];i++){
      num = put_prime_first[sum][k1][i];
      seq[24]=num%10;num/=10;
      seq[18]=num%10;num/=10;
      seq[12]=num%10;num/=10;
      seq[6]=num%10;num/=10;
      find_seq(step+1);
    }
    break;
  case 1:
    k3=seq[12];
    for (int i=1;i<=put_prime_third[sum][k3][0];i++){
      num = put_prime_third[sum][k3][i];
      seq[4]=num%10;num/=10;if (seq[4]==0) continue;
      seq[8]=num%10;num/=10;
      num/=10;
      seq[16]=num%10;num/=10;
      seq[20]=num%10;if (seq[20]==0) continue;
      find_seq(step+1);
    }
    break;
  case 2:
    k2=seq[6];k4=seq[8];
    for (int i=1;i<=put_prime_second_fourth[sum][k2][k4][0];i++){
      num = put_prime_second_fourth[sum][k2][k4][i];
      seq[9]=num%10;num/=10;
      num/=10;
      seq[7]=num%10;num/=10;
      num/=10;
      seq[5]=num%10;if (seq[5]==0) continue;
      find_seq(step+1);
    }
    break;
  case 3:
    k2=seq[16];k4=seq[18];
    for (int i=1;i<=put_prime_second_fourth[sum][k2][k4][0];i++){
      num = put_prime_second_fourth[sum][k2][k4][i];
      seq[19]=num%10;num/=10;
      num/=10;
      seq[17]=num%10;num/=10;
      num/=10;
      seq[15]=num%10;if (seq[15]==0) continue;
      find_seq(step+1);
    }
    break;
  case 4:
    seq[10]=sum-seq[0]-seq[5]-seq[15]-seq[20];if (seq[10]<=0 || seq[10]>=10) return;
    num=seq[0]*10000+seq[5]*1000+seq[10]*100+seq[15]*10+seq[20];if (!check[num]) return;
    seq[14]=sum-seq[4]-seq[9]-seq[19]-seq[24];if (seq[14]<0 || seq[14]>=10) return;
    num=seq[4]*10000+seq[9]*1000+seq[14]*100+seq[19]*10+seq[24];if (!check[num]) return;
    find_seq(step+1);
    break;
  case 5:
    k2=seq[7];k3=seq[12];k4=seq[17];
    for (int i=1;i<=put_prime_second_third_fourth[sum][k2][k3][k4][0];i++){
      num = put_prime_second_third_fourth[sum][k2][k3][k4][i];
      seq[22]=num%10;
      num/=10000;
      seq[2]=num%10;
      find_seq(step+1);
    }
    break;
  case 6:
    k1=seq[0];k3=seq[2];k5=seq[4];
    for (int i=1;i<=put_prime_first_third_fifth[sum][k1][k3][k5][0];i++){
      num = put_prime_first_third_fifth[sum][k1][k3][k5][i];
      num/=10;
      seq[3]=num%10;num/=10;if (seq[3]==0) continue;
      num/=10;
      seq[1]=num%10;num/=10;if (seq[1]==0) continue;
      find_seq(step+1);
    }   
    break;
  case 7: 
    k1=seq[20];k3=seq[22];k5=seq[24];
    for (int i=1;i<=put_prime_first_third_fifth[sum][k1][k3][k5][0];i++){
      num = put_prime_first_third_fifth[sum][k1][k3][k5][i];
      num/=10;
      seq[23]=num%10;num/=10;
      num/=10;
      seq[21]=num%10;num/=10;
      find_seq(step+1);
    }
    break;
  case 8:
    seq[11]=sum-seq[1]-seq[6]-seq[16]-seq[21];if (seq[11]<0 || seq[11]>=10) return;
    num=seq[1]*10000+seq[6]*1000+seq[11]*100+seq[16]*10+seq[21];if (!check[num]) return;
    seq[13]=sum-seq[3]-seq[8]-seq[18]-seq[23];if (seq[13]<0 || seq[13]>=10) return;
    num=seq[3]*10000+seq[8]*1000+seq[13]*100+seq[18]*10+seq[23];if (!check[num]) return;
    num=seq[10]*10000+seq[11]*1000+seq[12]*100+seq[13]*10+seq[14];if (!check[num]) return;
    ans_num++;
    for (int i=0;i<25;i++)
      ans[ans_num][i]=seq[i];
    break;
  }
}
int main(){
  fin >> sum >> seq[0];
  euler();//利用欧拉筛法寻找素数
  get_put_prime();//得到每位数字之和为i,开头数字为j,这样的素数的第k个的素数表
  find_seq(0);
  for (int i=1;i<ans_num;i++)
    for (int j=i+1;j<=ans_num;j++)
      if (check_func(i,j)){
    int tmp;
    for (int k=0;k<25;k++){
      tmp=ans[i][k];
      ans[i][k]=ans[j][k];
      ans[j][k]=tmp;
    }
      }
  print();
  return 0;
}
额,提交了6次,把continue的地方用成了return,这。。我也无奈了
不过速度小快。
这道题目是典型的搜索优化题目,接下来就循序渐进的讲一下算法,
题目大意:在五行五列矩阵25个格子内,填入0-9,使每一行、每一列、对角线都是素数。并且该5位素数第一位不能为0,且每个素数的各位之和为给定的数sum,题目还
告诉了我们第一行第一列的那个数必须为x
算法1:
枚举25个格子内的数,然后进行素数判断,这样至少需要10^25次运算,超时明显

算法2:
假设我们已经枚举了前四行,那么对于最后一行上某一列的数,可以由sum-这一列前四行所有的数之和得到,因此我们的枚举次数降到了10^20

算法3:
算法2也是不行滴,从题目中我们可以知道,相当于每行每列都要是一个5位素数,那么我们可不可以尝试填入5个5位素数呢?
可以的。由于5位素数有8392个,因此现在的时间复杂度为8392^5=4.1*10^19

算法4:
我们可以用算法2的思想来优化算法3,时间复杂度降到了4.96*10^15次
目测我们已经降低了10^9次,优化的力量真是强大

算法5:
我们已经知道了利用枚举素数的方法,那么能不能再做一些提高呢
这时候,需要回顾一下题目,它说已经规定了第一行第一列的那个数了,想一下,
可以发现,这时候我们只需要枚举第一位已知的素数即可,这样的素数最多为106个
我们先得到第一行,然后得到第一列,接着从第一行得到的枚举每一列,
那么时间复杂度差不多为10^12

算法6:
算法5提供了我们一种思路,如果已知某一行或者某一列上的几个数,
那么我们只需要枚举已知这几位的素数即可
可以知道,已知2个位置上的数的5为素数最多11个,已知3个位置上的数的素数最多3个,通过不同的扩展方式
我们可以扩展出8.1*10^8时间复杂度的算法,也就是我的代码的算法,
接下来靠大家仔细研究吧。











   

你可能感兴趣的:(c,算法,优化,user,扩展)