Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Lilypad Pond [USACO07FEB] – Shortest Path Solution

Notes 1

A great problem that requires building a graph in a non-obvious way. Problem link

At first glance, it's not obvious to build a graph, but with some thought it's not too hard.

The idea is to set edge weights to 0 for moving onto lily pads and 1 for moving on to water cells. Then just run Dijkstra. That should be straightforward, and indeed it took only 20 minutes to code.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 45 * 45;
const int MAXM = 10000000;
const int INF = 0x3f3f3f3f;

struct Edge {
    int to, val, nxt;
} e[MAXM];

struct Node {
    int to, val;
    bool operator < (const Node &other) const {
        return val > other.val;
    }
};

priority_queue<Node> pq;
bool vis[MAXN];
int head[MAXN], cnt, n, m, _mp[50][50], start, dest, dis[MAXN], ways[MAXN];
const int dx[] = {1,1,-1,-1,2,2,-2,-2};
const int dy[] = {2,-2,2,-2,1,-1,1,-1};

inline int id(int x, int y) { return (x-1)*m + y; }

void addEdge(int u, int v, int w) {
    e[++cnt] = {v, w, head[u]};
    head[u] = cnt;
}

void build(int x, int y) {
    if (_mp[x][y] == 3) start = id(x,y);
    if (_mp[x][y] == 4) dest = id(x,y);
    for (int i = 0; i < 8; ++i) {
        int nx = x + dx[i], ny = y + dy[i];
        if (nx < 1 || ny < 1 || nx > n || ny > m) continue;
        if (_mp[nx][ny] == 2) continue;
        if (_mp[nx][ny] == 1 || _mp[nx][ny] == 4) addEdge(id(x,y), id(nx,ny), 0);
        if (_mp[nx][ny] == 0) addEdge(id(x,y), id(nx,ny), 1);
    }
}

void init() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            cin >> _mp[i][j];
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            build(i, j);
}

void dijkstra() {
    init();
    memset(dis, 0x3f, sizeof(dis));
    memset(ways, 0, sizeof(ways));
    dis[start] = 0; ways[start] = 1;
    pq.push({start, 0});
    while (!pq.empty()) {
        int u = pq.top().to; pq.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (vis[v]) continue;
            if (dis[v] > dis[u] + e[i].val) {
                dis[v] = dis[u] + e[i].val;
                ways[v] = ways[u];
                pq.push({v, dis[v]});
            } else if (dis[v] == dis[u] + e[i].val) {
                ways[v] += ways[u];
            }
        }
    }
    if (ways[dest] == 0) cout << -1 << endl;
    else cout << dis[dest] << endl << ways[dest] << endl;
}

int main() {
    dijkstra();
    return 0;
}

This code only gets 50 points. After checking, the issue is that the problem asks for the number of ways to place lily pads, not the number of shortets paths. So we need a different approach.

How to solve? Use DFS to build edges only from water or start cells. If we encounter a lily pad, treat it as part of the water cell it originated from, so we build edges from the oriignal water cell through the lily pad to other cells. Plus some extra details.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 51 * 51;
const int MAXM = 10000000;
const int INF = 0x3f3f3f3f;

struct Edge {
    int to, val, nxt;
} e[MAXM];

struct Node {
    int to, val;
    bool operator < (const Node &other) const {
        return val > other.val;
    }
};

priority_queue<Node> pq;
bool vis[MAXN], visited[MAXN][MAXN];
int head[MAXN], cnt, n, m, _mp[50][50], start, dest, dis[MAXN];
ll ways[MAXN];
const int dx[] = {1,1,-1,-1,2,2,-2,-2};
const int dy[] = {2,-2,2,-2,1,-1,1,-1};

inline int id(int x, int y) { return (x-1)*m + y; }

void addEdge(int u, int v, int w) {
    e[++cnt] = {v, w, head[u]};
    head[u] = cnt;
}

void dfs(int x, int y, int orig) {
    visited[x][y] = 1;
    for (int i = 0; i < 8; ++i) {
        int nx = x + dx[i], ny = y + dy[i];
        if (nx < 1 || ny < 1 || nx > n || ny > m) continue;
        if (visited[nx][ny] || _mp[nx][ny] == 2) continue;
        if (_mp[nx][ny] == 1) dfs(nx, ny, orig);
        else {
            visited[nx][ny] = 1;
            addEdge(orig, id(nx, ny), 1);
        }
    }
}

void build(int x, int y) {
    if (_mp[x][y] == 3) start = id(x,y);
    if (_mp[x][y] == 4) dest = id(x,y);
    if (_mp[x][y] == 0 || _mp[x][y] == 3 || _mp[x][y] == 4) {
        memset(visited, 0, sizeof(visited));
        dfs(x, y, id(x,y));
    }
}

void init() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            cin >> _mp[i][j];
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            build(i, j);
}

void dijkstra() {
    init();
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(ways, 0, sizeof(ways));
    dis[start] = 0; ways[start] = 1;
    pq.push({start, 0});
    while (!pq.empty()) {
        int u = pq.top().to; pq.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (vis[v]) continue;
            if (dis[v] > dis[u] + e[i].val) {
                dis[v] = dis[u] + e[i].val;
                ways[v] = ways[u];
                pq.push({v, dis[v]});
            } else if (dis[v] == dis[u] + e[i].val) {
                ways[v] += ways[u];
            }
        }
    }
    if (ways[dest] == 0) cout << -1 << endl;
    else cout << dis[dest] - 1 << endl << ways[dest] << endl;
}

int main() {
    dijkstra();
    return 0;
}

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.