ETH官方钱包

創作內容

0 GP

USACO IOI題 prime3

作者:willyliu│2010-02-08 07:38:16│巴幣:0│人氣:498
這題真的要讓我大爆炸啦!
由於本人不怎麼精明,剪枝不太會減,所以一直TLE
題目:
製造出5X5方陣,每行(由上而下),每列(左而右),兩條對角線(左而右),皆是五位數(10000<=n<=99999)質數。
而且每一位數(digit)的和(sum)都是一個指定的數字a,且左上角(0, 0)的數字也是指定的數字b
如果有多組解,由r0 c0,r0c1,...的大小依序排列,每組解空一行輸出,最後無空行。

有兩個出發點:
1.產生所有可能的方陣,檢驗是否符合條件。
2.產生所有可能的質數,放上去,檢驗條件。
顯然,五位數的質數密度"dπ(x) /dx"是很低的,第二種方法效率高多了。

於是,先產生質數表,然後帶進去。
問題是,順序呢?要怎麼帶才能早一點進行檢驗、早一點剪枝?
很明顯的,要是用r0, r1, r2, r3, r4絕對會爆掉,我用的方法是c0, r0, c1, r1...
雖然要做10次,但是數字的自由度仍然是14(25維 - 左上角指定數字-行、列、對角線的限制)
做10次、代表早一點剪枝。
問題是,仍然會TLE,最大的測資(數字和23)會跑5秒以上。
於是,要讓每一層的迭代複雜度愈小愈好。
我於是做了一個位數->整數的映射表例如NUM(a1, a2, a3, a4, a5)可以得到一個五位數
以及整數->位數的應射表,一堆恐怖的預處理。
還是不太夠,時間會到3 ~ 5秒,但限制是2秒

於是,我做了一件常人不會做,非??植赖氖虑?-------用template展開迴圈和遞迴
由於5x5這個大小是固定的,是個編譯期常數,我們就可以把任何for(int i = 0; i <5; ++i)這樣的迴圈展開
還有,任何關於這裡是第幾行、第幾列的 if 檢驗式也可以用template特化來做。
以下是噁心的程式碼,他在最後一筆(和為23)以1.642秒驚險過關了(USACO的電腦還蠻快的)。
希望各位看得懂template 在做什麼、是怎麼運作的。
有興趣可以抓下來編譯,輸入檔(prime3.in)的格式是:
(數字和) (左上角數字)
/*
PROG: prime3
LANG: C++
*/
#include<cstdio>
#include<algorithm>
#define NUM(A,B,C,D,E) (number[A][B][C][D][E])
using namespace std;
static const int MAX_P = 100000;
static const int MAX_S = 10000;
static const int size = 5;
bool notprime[MAX_P];
bool islegalprime[MAX_P];
bool islegalprimenotzero[MAX_P];
int primes[MAX_P / 10];
int primeshead[MAX_P / 10];
int number[10][10][10][10][10];
int parse[MAX_P][size];
int div10[MAX_P];

int *pstart[5][MAX_P];
int *phstart[11];
int *pp = primes;
int *pph = primeshead;
int solutions[MAX_S][size][size];
int ssize;
char matrix[size][size];
int h, sum;
int counter[size * 2];
int sols[MAX_S];
int colnum[size];
int colsum[size];
int rownum[size];
int rowsum[size];

static const int pow10i[] = {10000, 1000, 100, 10, 1};
/*參數分別是:第幾層遞迴、第幾行/列、遞迴上界*/
template<int N = 0, int IND = 0, int UPPER = size>
struct operate
{
    static inline void OP1()
    {
        copy(matrix[N], matrix[N] + size, solutions[ssize][N]);
/*進行下一層遞迴*/
        operate<N + 1, IND, UPPER>::OP1();
    }
    static inline void OP2()
    {
            colnum[N] *= 10;
            colnum[N] += matrix[IND][N];
            operate<N + 1, IND, UPPER>::OP2();
    }
    static inline void OP3()
    {
            colnum[N] = div10[colnum[N]];
            operate<N + 1, IND, UPPER>::OP3();
    }
    static inline void OP4(int i)
    {
            matrix[IND][size - N - 1] = parse[i][N];
            operate<N + 1, IND, UPPER>::OP4(i);
    }
    static inline void OP5(int i)
    {
            matrix[size - N - 1][IND] = parse[i][N];
            operate<N + 1, IND, UPPER>::OP5(i);
    }
    static inline void OP6()
    {
            rownum[N] *= 10;
            rownum[N] += matrix[N][IND];
            operate<N + 1, IND, UPPER>::OP6();
    }
    static inline void OP7()
    {
            rownum[N] = div10[rownum[N]];
            operate<N + 1, IND, UPPER>::OP7();
    }
};
template<int IND, int UPPER>
/*遞迴達到上界(UPPER),不做任何事*/
struct operate<UPPER, IND, UPPER>
{
    static inline void OP1()
    {
    }
    static inline void OP2()
    {
    }
    static inline void OP3()
    {
    }
    static inline void OP4(int)
    {
    }
    static inline void OP5(int)
    {
    }
    static inline void OP6()
    {
    }
    static inline void OP7()
    {
    }
};
/*對解答排序的比較函數*/
bool cmp(const int a, const int b)
{
    for(int i = 0; i < size; ++i)
        for(int j = 0; j < size; ++j)
        {
            if(solutions[a][i][j] < solutions[b][i][j]) return true;
            else if(solutions[b][i][j] < solutions[a][i][j]) return false;
        }
    return false;
}
/*是否為符合條件的質數*/
inline bool legalprime(int n, bool allow0 = true)
{
    if(n < MAX_P / 10 || notprime[n]) return false;
    int s = 0;
    while(n > 0)
    {
        if(!allow0 && n % 10 == 0) return false;
        s += n % 10;
        n /= 10;
    }
    return s == sum;
}
/*Binary search找出前pos位數字為head的質數的下界*/
inline int* select(int pos, int head, int *prime, int *end)
{
    int s = 0, e = end - prime;
    int ans = e;
    while(s <= e)
    {
        int mid = s + e >> 1;
        if(prime[mid] / pow10i[pos] < head)
        {
            s = mid + 1;
        }
        else
        {
            ans = mid;
            e = mid - 1;
        }
    }
    return prime + ans;
}
/*找到解,複製解答到solutions裡*/
inline void setAns()
{
    operate<>::OP1();
    sols[ssize] = ssize;
    ++ssize;
}
/*以下是爆搜*/
template<int ind> 
inline void processCol();
template<int ind> 
inline void processRow()
{
    int j;
    ++counter[ind * 2 + 1];
    static const int pos = ind;
    int head = rownum[ind];
    int *i = pstart[pos][head];
    int *end = pstart[pos][head + 1];
    for(; i != end; ++i)
    {
        int cur = *i;
        static const int high = size - pos - 1;
        operate<0, ind, high>::OP4(*i);
        bool toprune = false;
        operate<0, ind>::OP2();
        if(!toprune) processCol<ind + 1>();
        operate<0, ind>::OP3();
    }
}
template<>
inline void processRow<4>()
{
        int s = NUM(matrix[0][0], matrix[1][1], matrix[2][2], matrix[3][3], matrix[4][4]);
        if(!islegalprime[s]) return;
        s = NUM(matrix[size - 1][0], matrix[size - 1][1], matrix[size - 1][2], matrix[size - 1][3], matrix[size - 1][4]);
        if(!islegalprime[s]) return;
        setAns();
}
template<int ind>
inline void processCol()
{
    ++counter[ind * 2];
    static const int pos = ind - 1;
    int head = colnum[ind];
    int *i = pstart[pos][head];
    int *end = pstart[pos][head + 1];
    for(; i != end; ++i)
    {
        static const int high = size - pos - 1;
        operate<0, ind, high>::OP5(*i);
        bool toprune = false;
        operate<0, ind>::OP6(); 
        if(!toprune) processRow<ind>();
        operate<0, ind>::OP7();
    }
}
/*第2行的時後檢查左下-右上對角線*/
template<>
inline void processCol<2>()
{
    static const int ind = 2;
    ++counter[4];
    static const int pos = 1;
    int head = colnum[2];
    int *i = pstart[pos][head];
    int *end = pstart[pos][head + 1];
    for(; i != end; ++i)
    {
        static const int high = size - pos - 1;
        for(int j = 0; j < high; ++j)
        matrix[size - j - 1][2] = parse[*i][j];
        int s = NUM(matrix[4][0], matrix[3][1], matrix[2][2], matrix[1][3], matrix[0][4]);
        if(!islegalprime[s]) continue;
        bool toprune = false;
        operate<0, ind>::OP6();
        if(!toprune) processRow<2>();
        operate<0, ind>::OP7();
    }
}
/*第0行、列不允許有0出現的特例*/
inline void row0()
{
    ++counter[1];
    int *start = phstart[h];
    int *end = phstart[h + 1];
    for(int *i = start; i != end; ++i)
    {
        int cur = *i;
        operate<0, 0>::OP4(*i);
        operate<0, 0>::OP2();
        processCol<1>();
        operate<0, 0>::OP3();
    }
}
inline void col0()
{
    ++counter[0];
    int *start = phstart[h];
    int *end = phstart[h + 1];
    for(int *i = start; i != end; ++i)
    {
        int cur = *i;
        operate<0, 0>::OP5(*i);
        operate<0, 0>::OP6();
        row0();
        operate<0, 0>::OP7();
    }
}
/*開始的按鈕*/
void start()
{
    matrix[0][0] = h;
    for(int i = 0; i < size; ++i) 
        rownum[i] = colnum[i] = 0;
    col0();
}
int main()
{
    FILE *fin = fopen("prime3.in", "r"), *fout = fopen("prime3.out", "w");
    fscanf(fin, "%d%d\n", & sum, &h);
    for(int i = 2; i != MAX_P; ++i)
    {
        if(!notprime[i]) 
            for(long long j = (long long) i * i; j < MAX_P; j += i)
                notprime[j] = true;
    }
    for(int i = MAX_P / 10; i != MAX_P; ++i)
    {
        if(legalprime(i)) 
        {
            islegalprime[i] = true;
            *(pp++) = i;
        }
        if(legalprime(i, false)) 
        {
            islegalprimenotzero[i] = true;
            *(pph++) = i;
        }
    }
    /* precompute the lower-bound*/
    for(int i = 1; i <= 10; ++i)
        pstart[0][i] = select(0, i, primes, pp);
    for(int i = 10; i <= 100; ++i)
        pstart[1][i] = select(1, i, primes, pp);
    for(int i = 100; i <= 1000; ++i)
        pstart[2][i] = select(2, i, primes, pp);
    for(int i = 1000; i <= 10000; ++i)
        pstart[3][i] = select(3, i, primes, pp);
    for(int i = 10000; i <= 100000; ++i)
        pstart[4][i] = select(4, i, primes, pp);
    for(int i = 1; i <= 10; ++i)
        phstart[i] = select(0, i, primeshead, pph);
/*整數->位數 以及位數->整數的映射表*/
    for(int i = 0; i < 10; ++i)
        for(int j = 0; j < 10; ++j)
            for(int k = 0; k < 10; ++k)
                for(int l = 0; l < 10; ++l)
                    for(int m = 0; m < 10; ++m)
                    {
                        int cur = i * 10000 + j * 1000 + k * 100 + l * 10 + m;
                        number[i][j][k][l][m] = cur;
                        parse[cur][4] = i;
                        parse[cur][3] = j;
                        parse[cur][2] = k;
                        parse[cur][1] = l;
                        parse[cur][0] = m;
                    }
/*對數字除以10的預處理(除法很慢)*/
    for(int i = 0; i < MAX_P; ++i)
        div10[i] = i / 10;
    start();
/*對解答排序*/
    sort(sols, sols + ssize, cmp);
/*輸出*/
    for(int i = 0; i < ssize; ++i)
    {
        for(int j = 0; j < size; ++j)
        {
            for(int k = 0; k < size; ++k)
            {
                putc('0' + solutions[sols[i]][j][k], fout);
            }
            putc('\n', fout);
        }
        if(i != ssize - 1) putc('\n', fout);
    }
/*若無解*/
    if(ssize = 0) fprintf(fout, "NONE\n");
/*這邊是分析用的計數器*/
    for(int i = 0; i < size * 2; ++i)
        printf("ind:%d times: %d\n", i, counter[i]);
    return 0;
}
引用網址:http://www.jamesdambrosio.com/TrackBack.php?sn=249597
All rights reserved. 版權所有,保留一切權利

相關創作

同標籤作品搜尋:|program|programming|程式設計|程式|USACO|

留言共 0 篇留言

我要留言提醒:您尚未登入,請先登入再留言

喜歡★a27268139 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:爆肝啦!USACO 4-... 後一篇:傳說中的TMP---Te...


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情? 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】