Google Code Jam 2015 QR

本文深入探讨了算法实现和具体技术应用,包括但不限于Dijkstra算法优化、Ominous Omino问题解决策略、字符操作与字符串处理技巧。文章详细阐述了解决方案、核心逻辑与优化方法,旨在为读者提供深入理解与实际应用的指南。

牛人自有牛人的做法,弱渣也有弱渣的做法。。。分享一下。。

Problem C. Dijkstra

因为ijk = -1

那么我们只需要找prefix的i和suffix的k即可。

而发现一共就4种运算符,所以最多8位一个循环,那么我就if X > 20 then X = 20

重载了一下运算符号,这样就能避免很多不必要的麻烦写法

昂神教我的,既然是乘法那么正负符号分开算好了。。

char str[1000000];
string L , a;
LL n , x;
int c[][4] =  { {0 , 1 , 2 , 3},
               {1 , 0 , 3 , 2},
               {2 , 3 , 0 , 1},
               {3 , 2 , 1 , 0}};
int neg[][4] = {{1 , 1 , 1 , 1},
               {1 ,-1 , 1 ,-1},
               {1 ,-1 ,-1 , 1},
               {1 , 1 ,-1 ,-1}};
int f(char c){
    if (c == 'i') return 1;
    if (c == 'j') return 2;
    return 3;
}
struct Number{
    int ele , op;
    void ONE(){
        ele = 0;
        op = 1;
    }
    Number(){}
    Number(int c){
        ele = c;
        op = 1;
    }
    Number operator * (const Number & A) const{
        Number ret;
        ret.op = op * neg[ele][A.ele] * A.op;
        ret.ele = c[ele][A.ele];
        return ret;
    }
};
void solve(){
    cin >> n >> x;
    a = RS(str);
    printf("Case #%d: " , ++Case);
    Number pre;
    pre.ONE();
    for (int i = 0 ; i < n ; ++i){
        pre = pre * Number(f(a[i]));
    }
    Number res;res.ONE();
    LL k = x;
    while(k)
    {
        if(k&1) res = res * pre;
        pre = pre * pre, k >>= 1;
    }
    if (res.ele != 0 || res.op != -1){
        puts("NO");
        return;
    }
    L = "";
    if (x > 20) x = 20;
    DO(x) L += a;
    int left = -1 , right = -1;
    res.ONE();
    for (int i = 0 ; i < L.length() ; ++i){
        res = res * Number(f(L[i]));
        if (res.ele == 1 && res.op == 1){
            left = i;
            break;
        }
    }
    res.ONE();
    for (int i = L.length() - 1 ; i >= 0 ; --i){
        res = Number(f(L[i])) * res;
        if (res.ele == 3 && res.op == 1){
            right = i;
            break;
        }
    }
//    cerr << left << ' ' << right << endl;
    puts((left != -1 && right != -1 && left < right) ? "YES" : "NO");
}

Problem D. Ominous Omino

从来没有枚举对过。所以从一开始就没打算去枚举。。

一看,<=20,打表啊!公司机器跑的飞快!

我一看到题,看到这个图


就知道X>=7肯定无解啊,然后就sb地忘了写了23333.

首先是如何枚举Domino块呢,我们想如果按照X = 1 , 2 , 3 。。。 的顺序枚举的话,那么我们只用在 X -> X + 1 的时候枚举一块在已有的Domino周围转一圈,然后去重即可。(我是按照Left-Top的地方戳齐)

然后就是非常暴力地模拟。。找到每一块,然后枚举放在哪个地方,然后剩下的搜。。

怎么搜呢,可以直接再枚举一遍所有的块所有可以放的位置,然后就是一个Dancing Links X Method里面的精确覆盖问题(数据范围那么小是吧。。。)

搜到X = 6 , R = 4 , C = 18 的时候TMD搜不出来了!!于是发现如果 R * C % X == 0 并且 r <= R , c <= C 的 X , r , c 答案是 GABRIEL 的话,那么 R , C 肯定也是。。方法是——


按照这种放法肯定能放下的吧2333

const int maxM = 25 * 25, maxN = 760 * 6 * 6 + 5;
const int max_size = maxN * maxM;
const int inf = 0x3f3f3f3f;
int L[max_size], R[max_size], U[max_size], D[max_size], CH[max_size], RH[max_size];
int S[maxM], O[maxM];
int head, size;
int node(int up, int down, int left, int right) {
    U[size] = up, D[size] = down;
    L[size] = left, R[size] = right;
    D[up] = U[down] = R[left] = L[right] = size;
    return size++;
}
bool mat[maxN][maxM];
void init(int N, int M) {
    size = 0;
    head = node(0, 0, 0, 0);
    for (int j = 1; j <= M; ++j) {
        CH[j] = node(size, size, L[head], head), S[j] = 0;
    }
    for (int i = 1; i <= N; ++i) {
        int row = -1, k;
        for (int j = 1; j <= M; ++j) {
            if (!mat[i][j]) continue;
            if (row == -1) {
                row = node(U[CH[j]], CH[j], size, size);
                RH[row] = i, CH[row] = CH[j], ++S[j];
            } else {
                k = node(U[CH[j]], CH[j], L[row], row);
                RH[k] = i, CH[k] = CH[j], ++S[j];
            }
        }
    }
}
void remove(const int &c) {
    L[R[c]] = L[c], R[L[c]] = R[c];
    for (int i = D[c]; i != c; i = D[i]) {
        for (int j = R[i]; j != i; j = R[j]) {
            U[D[j]] = U[j], D[U[j]] = D[j];
            --S[CH[j]];
        }
    }
}
void resume(const int &c) {
    for (int i = U[c]; i != c; i = U[i]) {
        for (int j = L[i]; j != i; j = L[j]) {
            ++S[CH[j]];
            U[D[j]] = D[U[j]] = j;
        }
    }
    L[R[c]] = R[L[c]] = c;
}
int len;
bool DLX(const int &k) {
    if (R[head] == head) {
        len = k - 1;
        return true;
    }
    int s = inf, c;
    for (int t = R[head]; t != head; t = R[t]) {
        if (S[t] < s) s = S[t], c = t;
    }
    remove(c);
    for (int i = D[c]; i != c; i = D[i]) {
        O[k] = RH[i];
        for (int j = R[i]; j != i; j = R[j]) {
            remove(CH[j]);
        }
        if (DLX(k + 1)) {
            return true;
        }
        for (int j = L[i]; j != i; j = L[j]) {
            resume(CH[j]);
        }
    }
    resume(c);
    return false;
}
int X , RR , CC;
const int DN = 7;
typedef set<PII> :: iterator DITER;
struct Domino{
    set<PII> a;
    bool operator < (const Domino & A) const{
        for(set<PII> :: iterator ia = a.begin() , ib = A.a.begin() ; ia != a.end() && ib != A.a.end() ; ++ia , ++ib){
            if ((*ia) > (*ib)) return false;
            if ((*ia) < (*ib)) return true;
        }
        return false;
    }
    void output(){
        for ( DITER i = a.begin() ; i != a.end() ; ++i)
            cerr << '(' << i -> fi << ',' << i -> se << ')' << ',';
        cerr << endl;
    }
    Domino rot(){
        Domino ret;
        ret.a.clear();
        PII lt = MP(10 , 10);
        for(DITER i = a.begin() ; i != a.end() ; ++i){
            checkMin(lt.fi , -i -> se);
            checkMin(lt.se , i -> fi);
        }
        for(DITER i = a.begin() ; i != a.end() ; ++i){
            ret.a.insert(MP(- i->se - lt.fi , i->fi - lt.se));
        }
        return ret;
    }
}now;
set<Domino> PSet , DSet , checkList;

void Insert(Domino cur){
    PII lt = MP(10 , 10) , nt;
    for (DITER iter = cur.a.begin() ; iter != cur.a.end() ; ++iter){
        checkMin(lt.fi , iter -> fi);
        checkMin(lt.se , iter -> se);
    }
    for (DITER i = cur.a.begin() ; i != cur.a.end() ; ++i){
        for (int d = 0 ; d < 4 ; ++d){
            PII g = MP(i -> fi + dx4[d] , i -> se + dy4[d]);
            if (cur.a.find(g) != cur.a.end()) continue;
            nt = MP(min(lt.fi , g.fi) , min(lt.se , g.se));
            Domino add;
            add.a.clear();
            for ( DITER j = cur.a.begin() ; j != cur.a.end() ; ++j){
                add.a.insert(MP(j -> fi - nt.fi , j -> se - nt.se));
            }
            add.a.insert(MP(g.fi - nt.fi , g.se - nt.se));
            DSet.insert(add);
        }
    }
}
void init(){
    if (X == 1){
        now.a.clear();
        now.a.insert(MP(0 , 0));
        DSet.insert(now);
    }
    else{
        PSet = DSet;
        DSet.clear();
        for (set<Domino> :: iterator iter = PSet.begin() ; iter != PSet.end() ; ++iter)
            Insert(*iter);
    }
    int Count = 0;
    for (set<Domino> :: iterator iter = DSet.begin() ; iter != DSet.end() ; ++iter){
        Domino e = *iter;
        cerr << (++Count) << ":\t";
        e.output();
    }
}
int A[25][25] , num[25][25];
int line;
bool inmap(int x , int y){
    return 0 <= x && x < RR && 0 <= y && y < CC;
}
bool putin(Domino d , int x , int y){
    ++Case;
    for(DITER i = d.a.begin() ; i != d.a.end() ; ++i){
        int gx = x + i -> fi , gy = y + i -> se;
        if (!inmap(gx , gy)) return false;
        A[gx][gy] = Case;
    }
    int c = 0;
    for (int i = 0 ; i < RR ; ++i)
        for (int j = 0 ; j < CC ; ++j)
        if (A[i][j] != Case)
            num[i][j] = ++c;
    if (c == 0) return true;
    line = 1;
    for (int i = 0 ; i < RR ; ++i){
        for (int j = 0 ; j < CC ; ++j){
            for (set<Domino> :: iterator k = DSet.begin() ; k != DSet.end() ; ++k){
                now = *k;
                bool ok = true;
                RST(mat[line]);
                for ( DITER z = now.a.begin() ; ok && z != now.a.end() ; ++z){
                    int gx = i + z -> fi , gy = j + z -> se;
                    if (!inmap(gx , gy) || A[gx][gy] == Case){
                        ok = false;
                        break;
                    }
                    mat[line][num[gx][gy]] = 1;
                    //cerr << line << ' ' << gx << ' ' << gy << ' ' << num[gx][gy] << endl;
                    //getchar();
                }
                if (ok) ++line;
            }
        }
    }
    line--;
    init(line , RR * CC - X);
    if (DLX(0))
        return true;
    return false;
}
bool canput(Domino d){
    if (checkList.find(d) != checkList.end()) return true;
    RST(A);
    Case = 0;
    for (int t = 0 ; t < 4 ; ++t){
        checkList.insert(d);
        d = d.rot();
    }
    for (int t = 0 ; t < 4 ; ++t){
        for (int i = 0 ; i < RR ; ++i)
            for (int j = 0 ; j < CC ; ++j)
                if (putin(d , i , j))
                return true;
        d = d.rot();
    }
    return false;
}
bool check(){
    checkList.clear();
    cerr << "CHECK" << ' ' << X << ' ' << RR << ' ' << CC << endl;
    for (set<Domino> :: iterator iter = DSet.begin() ; iter != DSet.end() ; ++iter){
        if (!canput(*iter)) return true;
    }
    return false;
}
const int MM = 20;
void solve(){
    init();
    vector<PII> okSet ;
    okSet.clear();
    for (RR = 1 ; RR <= MM ; ++RR)
    for (CC = RR ; CC <= MM ; ++CC){
        if (RR * CC % X != 0 || (RR < X && CC < X)){
            cout << "{" << X << ',' << RR << ',' << CC << "},";
            cout << "{" << X << ',' << CC << ',' << RR << "},";
            cerr << "{" << X << ',' << RR << ',' << CC << "}" << endl;
            continue;
        }
        bool pass = false;
        for (int i = 0 ; i < okSet.size() ; ++i)
        if (okSet[i].fi <= RR && okSet[i].se <= CC){
            pass = true;
            continue;
        }
        if (pass) continue;
        if (check()){
            cout << "{" << X << ',' << RR << ',' << CC << "},";
            cout << "{" << X << ',' << CC << ',' << RR << "},";
            cerr << "{" << X << ',' << RR << ',' << CC << "}" << endl;
        }
        else{
            okSet.PB(MP(RR , CC));
        }
    }
}










先展示下效果 https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值