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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值