GCJ2019 Round1A 题解

本文探讨了在小数据集上使用暴力算法解决问题的方法,以及在大数据集上利用字典树进行优化的策略。通过具体实例展示了如何构建字典树,并采用反向构造的方式解决特定问题。同时,介绍了如何在字典树中实现节点分割,以提高搜索效率。

A.pylons
小数据暴力,大数据根据ore定理用延长通路法构造解,这里参考了
https://www.cnblogs.com/zhj5chengfeng/p/3233992.html
这篇blog的总结,赛内我犯了sb错误,把下标当数用了当场GG,然后,然后就没了

#include<bits/stdc++.h>
using namespace std;
struct edge{
    int v,nxt;
}e[200000];
int h[405];
int vis[405];
int deg[405];
stack<int>ans;
int cnt=0;
void init(){
    memset(e,-1,sizeof e);
    memset(h,-1,sizeof h);
    memset(vis,0,sizeof vis);
    memset(deg,0,sizeof deg);
    while(!ans.empty())ans.pop();
    cnt=0;
}
void add(int u,int v){
    e[cnt].v=v;
    e[cnt].nxt=h[u];
    h[u]=cnt++;
}
struct Hamilton_Circuit {
    static const int N=405;

    bool G[N][N], vs[N];
    int n, next[N], head, tail;

    void init(int _n) {
        n=_n;
        memset(G, 0, sizeof G);
        for(int i=0;i<_n;i++){
            for(int j=h[i];~j;j=e[j].nxt){
                G[i][e[j].v]=1;
            }
        }
    }
    void DFS_Head(int u) {
        vs[u]=1;
        for(int i=0; i<n; i++) if(G[i][u] && !vs[i]) {
            next[i]=u;
            DFS_Head(i);
            return;
        }
        head=u;
    }

    void DFS_Tail(int u) {
        vs[u]=1;
        for(int i=0; i<n; i++) if(G[u][i] && !vs[i]) {
            next[u]=i;
            DFS_Tail(i);
            return;
        }
        tail=u;
    }

    void Reverse(int u) {
        for(int i=next[u], temp, last=-1; i!=-1; i=temp) {
            temp=next[i];
            next[i]=last;
            last=i;
        }
        int temp=tail;
        tail=next[u];
        next[u]=temp;
    }

    int Find(int u) {
        for(int i=head; i!=-1; i=next[i]) {
            if(G[u][next[i]]) return i;
        }
        return -1;
    }

    bool Extend(int u) {
        if(G[u][head]) {
            next[u]=head;
            return 1;
        }
        int pre=Find(u);
        if(pre==-1) return 0;
        next[u]=next[pre];
        next[tail]=head;
        next[tail=pre]=-1;
        return 1;
    }

    void Solve() {
        memset(next, -1, sizeof next);
        memset(vs, 0, sizeof vs);
        DFS_Head(0), DFS_Tail(0);
        int Len=1;
        for(int i=head; i!=tail; i=next[i], Len++);
        for(int i; 1; ) {
            if(!G[tail][head]) {
                for(i=next[head]; !(G[i][tail] && G[next[i]][head]); i=next[i]);
                Reverse(i);
            }
            if(Len==n) break;
            for(i=0; i<n; i++) if(!vs[i] && Extend(i)) {
                head=i, vs[i]=1, Len++;
                break;
            }
        }
    }

    void PRINT(int c) {
        for(int i=head; head!=0; i=next[i]) {
            next[tail]=head;
            tail=head;
            head=next[head];
            next[tail]=-1;
        }
        for(int i=head; i!=-1; i=next[i]) {
            printf("%d %d\n", i/c+1,(i%c)+1);
            //if(next[i]==-1) printf("1 1\n");
        }
    }
};

Hamilton_Circuit fuck;

int dfs(int rt,int cnt,int n){
    //printf("%d %d\n",rt,cnt);
    if(cnt==n){
        ans.push(rt);
        return 1;
    }
    for(int i=h[rt];~i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!vis[v]){
            vis[v]=1;
            if(dfs(v,cnt+1,n)){
                ans.push(rt);
                return 1;
            }
            vis[v]=0;
        }
    }
    return 0;
}
int main(){
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        int r,c,n;
        init();
        scanf("%d%d",&r,&c);
        n=r*c;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                int ux=i/c,uy=i%c;
                int vx=j/c,vy=j%c;
                if(ux==vx||uy==vy||(ux-uy)==(vx-vy)||(ux+uy)==(vx+vy))continue;
                deg[i]++;deg[j]++;
                add(i,j);add(j,i);
            }
        }
        int mn=n;
        for(int i=0;i<n;i++){
            mn=min(mn,deg[i]);
        }
        printf("Case #%d: ",cas);
        if(mn>=n/2){//construct
            puts("POSSIBLE");
            fuck.init(n);
            fuck.Solve();
            fuck.PRINT(c);
        }else{//search
            int f=0;
            for(int i=0;i<n;i++){
                vis[i]=1;
                if(dfs(i,1,n)){
                    f=1;
                    break;
                }
                vis[i]=0;
            }
            if(!f)puts("IMPOSSIBLE");
            else {puts("POSSIBLE");
            while(!ans.empty()){
                int qq=ans.top();
                ans.pop();
                printf("%d %d\n",qq/c+1,(qq%c)+1);
            }
            }
        }
    }
    return 0;
}

B 没看待补

C.Alien Rhyme
反向构造一颗字典树,对于一个节点,这层最多只能分出去一对,那么当前子树如果<4个就直接分割出去一对,dfs跑一跑就好了

#include<bits/stdc++.h>
using namespace std;
struct node{
    int c[26];
    int ans;
    int sum;
}tree[100000];
int cnt=0;
char str[55];
void init(){
    memset(tree,0,sizeof tree);
    cnt=0;
}
void Insert(int rt,int p){
    tree[rt].sum++;
    if(p==-1)return;
    int q=str[p]-'A';
    if(tree[rt].c[q]){
        Insert(tree[rt].c[q],p-1);
    }else{
        tree[rt].c[q]=++cnt;
        Insert(cnt,p-1);
    }
}
void solve(int rt){
    if(rt&&tree[rt].sum<=3){
        tree[rt].ans=tree[rt].sum/2;
        return ;
    }
    for(int i=0;i<26;i++){
        int son=tree[rt].c[i];
        if(!son)continue;
        solve(son);
        tree[rt].ans+=tree[son].ans;
    }
    int lft=tree[rt].sum-(tree[rt].ans<<1);
    if(rt&&lft>=2){
        tree[rt].ans++;
    }

}
int main(){

    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        int n;
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",str);
            Insert(0,strlen(str)-1);
        }
        solve(0);
        printf("Case #%d: %d\n",cas,tree[0].ans*2);
    }
    return 0;
}

### 问题分析 题目 [P13282 [GCJ 2013 Qualification] Lawnmower](https://www.luogu.com.cn/problem/P13282) 要求模拟一个自动割草机在网格上的移动路径,判断是否能够覆盖整个目标区域。该题本质上是一个模拟类问题,核心在于如何高效地表示网格状态,并通过模拟割草机的移动来更新其位置和方向。 割草机每次只能向一个方向移动一格,当遇到边界或已经访问过的格子时,则顺时针旋转90度并继续前进。任务是计算割草机完成所有可访问格子所需的操作次数[^1]。 ### 解题思路 1. **网格初始化**: - 使用一个二维数组 `grid` 表示地图,其中 `0` 表示未访问,`1` 表示已访问。 - 初始化起点 `(x, y)` 和初始方向 `dir`(例如:北、东、南、西)。 2. **方向控制**: - 使用方向数组 `dx[]` 和 `dy[]` 分别表示四个方向(北、东、南、西)对应的坐标变化。 - 当前方向使用一个整数索引表示,顺时针旋转时只需将索引加1并对4取模。 3. **移动与转向逻辑**: - 每次尝试向前移动一步,如果新位置超出边界或已经被访问,则改变方向(顺时针旋转90度),否则继续前进。 - 每次移动后标记当前格子为已访问,并记录操作次数。 4. **终止条件**: - 当所有格子都被访问完毕,或者割草机进入死循环无法继续前进时终止程序。 ### C++ 实现代码 以下是一个完整的 C++ 实现方案: ```cpp #include <iostream> #include <vector> #include <unordered_map> using namespace std; // 定义四个方向:北、东、南、西 int dx[] = {-1, 0, 1, 0}; int dy[] = {0, 1, 0, -1}; int main() { int n, m; cin >> n >> m; vector<vector<int>> grid(n, vector<int>(m, 0)); // 0表示未访问 int x = 0, y = 0; // 初始位置 int dir = 1; // 初始方向:东(对应dx[1], dy[1]) int steps = 0; while (true) { grid[x][y] = 1; // 标记当前位置为已访问 steps++; // 尝试前进一步 int nx = x + dx[dir]; int ny = y + dy[dir]; // 如果下一步越界或已被访问,则顺时针旋转90度 if (nx < 0 || nx >= n || ny < 0 || ny >= m || grid[nx][ny] == 1) { dir = (dir + 1) % 4; // 改变方向 nx = x + dx[dir]; ny = y + dy[dir]; // 再次尝试前进一步 if (nx < 0 || nx >= n || ny < 0 || ny >= m || grid[nx][ny] == 1) { break; // 已无路可走,结束循环 } } x = nx; y = ny; } cout << steps << endl; return 0; } ``` ### 复杂度分析 - **时间复杂度**:最坏情况下每个格子被访问一次,因此时间复杂度为 $O(n \times m)$。 - **空间复杂度**:需要存储一个 $n \times m$ 的二维数组,因此空间复杂度为 $O(n \times m)$。 ### 优化建议 - 可以引入一个计数器,记录已访问的格子数量,一旦达到总格子数即可提前终止循环。 - 使用 `bitset` 或 `bool` 类型数组代替 `int` 数组可以节省内存空间。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值