hdu 6223

(搜索+剪枝)
題意:給定n(n<2000000) 個點的帶環單向圖,每個點i 有一個權值Di(0Di9) 且只有一條出邊連接着 點(i2+1)modn ,求從哪一點出發,經過n 個點後,這條路徑上組成的數字最大。

這個題目有很多解法,正解是後綴數組。(目前我只想出怎麼用搜索解這個題)
思路1(搜索+剪枝):
  若想數字較大,那麼這個數字的最高位越大越好,再是次高位,再次次高…那麼我們可以先找出這些點中權值最大的點作爲起始點放入隊列中,然後一步步做廣度優先搜索,依次排除組成數字較小的起始點。最後剩下的那個點就是答案。
  但是這個肯定超時,複雜度最大爲O(n2) ,仔細想想會發現這個圖有很多特點:圖由許多單向鏈和環構成;有的鏈連接到了其他鏈的中間;所有鏈的末尾肯定連接一個環。而超時的原因肯定是許多點都被重複搜索多次了。這裏可能有一個點被多個起始點搜索過(鏈的分支出);一個點被一個起始點一直搜索(環)。
  於是朝這個方向優化:
  1.同一層(step)裏,我們只要那些當前權值最大的點對應的起始點。
  2.對於鏈的分支而言:假如初始點A 搜索到一個已經被點B 搜索過的點,那麼初始點B 就不用繼續搜索了(想想爲什麼),B 就可以被移出隊列。
  3.對於環而言,假如一個點搜索了一圈最後到自身,那麼我們便可以斷定這個點是最優的(想想爲什麼)。
  這三個優化缺一個都超時OTZ,折騰了我幾個小時…
思路2(後綴數組):待更
思路3(暴力枚舉):
  仔細觀察圖,這個圖裏面環很多,暴力枚舉每一個起始點,判斷前幾十位找出數字最大的點對應的起始點即可。

代碼1:

#include <cstdio>
#include <queue>
#include <cctype>
#include <cstring>
#include <algorithm>
#define LL long long

using namespace std;
const int maxn = 150010;

//  to whom the point blg(belong);
// rid = 1: means I can get rid of this point
int blg[maxn], rid[maxn];
int to[maxn], pre[maxn], mx_value[maxn];
char w[maxn];
struct node {
    int st_pos, cur_pos;
    node(int a=0, int b=0):st_pos(a),cur_pos(b){}
};
//queue<node> Q;
node Q[maxn<<1], tmp_que[maxn<<1];
int r;

void solve(int cs) {
    //init
    r = 0;
    int n;
    scanf("%d",&n); getchar();
    gets(w);
    for(int i=0; i<n; i++) {
        int v = ((LL)i*i + 1LL) % (LL)n;
        to[i] = v; pre[v] = i;
        Q[r++] = node(i, i);
        rid[i] = 0;
        mx_value[i] = blg[i] = -1;
    }
    //solve
    int step = 0, ans = -1;
    while(r > 1) {
        int sz = 0;
        // find the max number in the current step
        for(int i=0; i<r; i++) {
            node u = Q[i];
            if(rid[u.st_pos]) continue ;
            if((int)w[u.cur_pos] < mx_value[step]) continue ;
            if((int)w[u.cur_pos] > mx_value[step]) {
                mx_value[step] = (int)w[u.cur_pos];
                sz = 0;
            }
            int v = to[u.cur_pos];
            if(blg[v] != -1) rid[blg[v]] = 1;
            tmp_que[sz++] = node(u.st_pos, v);
        }
        // put these point into the queue again, step ++;
        for(int i=0; i<sz; i++) {
            Q[i] = tmp_que[i];
            int last = pre[Q[i].cur_pos];
            blg[last] = Q[i].st_pos;
            if(Q[i].st_pos == Q[i].cur_pos) {
                ans = Q[i].st_pos;
                break ;
            }
        }
        if(ans != -1) break ;
        r = sz;
        step ++; if(step >= n) break ;
    }
    // get the best starting point
    node u = Q[0];
    printf("Case #%d: ",cs);
    int pos = u.st_pos;
    if(ans != -1) pos = ans;
    for(int i=0; i<n; i++) {
        putchar(w[pos]);
        pos = to[pos];
    }
    puts("");
}

int main() {
    int cs, tot = 0;
    scanf("%d",&cs);
    while(cs --)
        solve(++tot);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章