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時間複雜度的算法,也就是我的代碼的算法,
接下來靠大家仔細研究吧。











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章