bzoj1698 [Usaco2007 Feb]Lilypad Pond 荷叶池塘 [BFS]

Description

为了便于牛们欣赏和锻炼,农夫JOHN在他的农场上新添加了一个美丽的池塘。 JOHN的池塘是一个长方形,他已经把它划分成了M行N列的小正方行 (1 <= M <= 30; 1 <= N <= 30). 某些正方行里是石头,另外一些则是特别结实的荷叶,其余则只有清水。 为了锻炼,Bessie想从一片荷叶跳到另外一片。她的每一次跳跃都是一个象棋中的马步:两行一列或一行两列。 JOHN看到了Bessie并且发现有时Bessie没有办法达到她的目标荷叶。他准备添加一些荷叶来让Bessie完成她的目标。当然,荷叶不能放在石头上。 帮助JOHN找出他最少要放多少片荷叶和他一共有多少种放最少片荷叶的方案。

Input

第1行: 两个整数, M 和 N。

第2~M+1行: 第i+1包含N个数,分别为第i行的N个格子的情况。 0表示格子为空,1表示有一片荷叶,2表示格子里有石头,3表示此格子是Bessie的起点,4 表示此格子是Bessie的目标。

Output

第1行: 一个数,最少情况下需要添加的荷叶数目。如果没有方案存在,输出- 1。

第2行: 一个数,达到最小值的方案总数。这个数保证不超过内设64位整数(long long/ int64)的大小。如果第一行是-1,不要输出此行。

Sample Input

4 5
1 0 0 0 0
3 0 0 0 0
0 0 2 0 0
0 0 0 4 0

输入解释:

池塘含4行5列。Bessie在第2行第1列并且想跳到第4行第4列。池塘里有1块石头和3片荷叶。

Sample Output

2
3

输出解释:

至少需要2片荷叶。一共有三种摆法:
第4行第2列,第2行第3列
第1行第3列,第3行第2列
第1行第3列,第2行第5列

 R1C2,R2C3  |   R1C3,R3C2  |   R1C3,R2C5
 1 0 0 0 0  |   1 0 X 0 0  |   1 0 X 0 0
 3 0 X 0 0  |   3 0 0 0 0  |   3 0 0 0 X
 0 0 2 0 0  |   0 X 2 0 0  |   0 0 2 0 0
 0 X 0 4 0  |   0 0 0 4 0  |   0 0 0 4 0

Solution

先说第一问:最少加几片荷叶。

很简单建图然后跑spfa:

每个点(x,y)向8个方向连边,如果点(nx,ny)是清水,则边权为1,如果是荷叶则边权为0。起点和终点都视为清水,最后答案-1即可。

第一问没什么难度,重点说第二问:有几种添荷叶的方案。

首先如果要求的是到终点的最短路径条数怎么做?只要在spfa松弛的时候判断一下:

if (dist[cur] + value == dist[nex]) {
    tot[nex] += tot[cur];
    push();
}
if (dist[cur] + value < dist[nex]) {
    dist[nex] = dist[cur] + value;
    tot[nex] = tot[nex];
    push();
}

但是这样求出的是“最短路径条数”,而不是“添加荷叶的方案数”,因为原图中有荷叶。这样我们就要改变建图的方式:

  • 每个清水向周围的清水连边
  • 对于每个荷叶,dfs它的8个方向,如果是荷叶继续dfs,如果是清水,则将它放入一个临时数组。
  • 这样,对于每一个荷叶的连通块都有一个临时数组,数组里的点两两连边。

就是这样,spfa的代码都不用改。

唉我这傻逼这么一道题写了两天。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

struct Edge{
    int to, v;
    Edge() {}
    Edge(int a, int b) : to(a), v(b) {}
};

int n, m;
int map[35][35];
vector<Edge> edges[1005];
int sx, sy, ex, ey;
int dir[8][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};

bool check(int x, int y) {
    if (x < 0 || x >= n || y < 0 || y >= m) return false;
    if (map[x][y] == 2) return false;
    return true;
}

int dist[1005];
long long ans[1005];
bool vis[1005];
void bfs() {
    queue<int> q;
    memset(dist, 0x3f, sizeof(dist));
    memset(ans, 0, sizeof(ans));
    memset(vis, 0, sizeof(vis));
    int s = sx * m + sy;
    q.push(s);
    dist[s] = 0;
    ans[s] = 1;
    vis[s] = 1;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (int i = 0; i < edges[cur].size(); i++) {
            Edge e = edges[cur][i];
            if (dist[cur] + e.v == dist[e.to]) {
                ans[e.to] += ans[cur];
                if (!vis[e.to]) {
                    if (e.to != ex * m + ey) {
                        vis[e.to] = 1;
                        q.push(e.to);
                    }
                }
            }
            if (dist[cur] + e.v < dist[e.to]) {
                dist[e.to] = dist[cur] + e.v;
                ans[e.to] = ans[cur];
                if (!vis[e.to]) {
                    if (e.to != ex * m + ey) {
                        vis[e.to] = 1;
                        q.push(e.to);
                    }
                }
            }
        }
    }
}

int tmp[1005], tot = 0;
int vis2[1005];
int used[1005][1005];
void dfs(int x, int y) {
    for (int i = 0; i < 8; i++) {
        int nx = x + dir[i][0];
        int ny = y + dir[i][1];
        if (!check(nx, ny)) continue;
        if (!map[nx][ny] && !vis2[nx * m + ny]) {
            vis2[nx * m + ny] = 1;
            tmp[tot++] = nx * m + ny;
        }
        if (map[nx][ny] == 1 && !vis[nx * m + ny]) {
            vis[nx * m + ny] = 1;
            dfs(nx, ny);
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    int t;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &map[i][j]);
            if (map[i][j] == 3) {
                sx = i;
                sy = j;
                map[i][j] = 0;
            }
            if (map[i][j] == 4) {
                ex = i;
                ey = j;
                map[i][j] = 0;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (map[i][j]) continue;
            for (int k = 0; k < 8; k++) {
                int nx = i + dir[k][0];
                int ny = j + dir[k][1];
                if (check(nx, ny) && !map[nx][ny])
                    edges[i * m + j].push_back(Edge(nx * m + ny, 1));
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (map[i][j] == 1 && !vis[i * m + j]) {
                tot = 0;
                memset(vis2, 0, sizeof(vis2));
                vis[i * j + 1] = 1;
                dfs(i, j);
                for (int k = 0; k < tot; k++) {
                    for (int l = k + 1; l < tot; l++) {
                        if (!used[tmp[k]][tmp[l]]) {
                            used[tmp[k]][tmp[l]] = used[tmp[l]][tmp[k]] = 1;
                            edges[tmp[k]].push_back(Edge(tmp[l], 1));
                            edges[tmp[l]].push_back(Edge(tmp[k], 1));
                        }
                    }
                }
            }
        }
    }
    bfs();
    if (dist[ex * m + ey] == 0x3f3f3f3f) {
        printf("-1\n");
        return 0;
    }
    printf("%d\n%lld\n", dist[ex * m + ey] - 1, ans[ex * m + ey]);
    return 0;
}
时间: 2024-10-28 15:59:24

bzoj1698 [Usaco2007 Feb]Lilypad Pond 荷叶池塘 [BFS]的相关文章

BZOJ 1698 [Usaco2007 Feb]Lilypad Pond 荷叶池塘 BFS+最短路

题意:链接 **方法:**BFS+最短路 解析: 这道题还是挺有意思的. 第一个想法被卡,第一发是二分+bfs,但是这样的话,会有什么处理不了呢?到一个点的流量. 所以就得换想法. 不妨重新定义最短路. 把图中所有的0的点看做可行点,每一次搜出从该点可以到打的0点,然后将这个边权视作1. 即f[i][j]代表到i,j这点的最短路. 显然终点特判: 然后同时记录一下最短路的数量即可. 代码: #include <queue> #include <cstdio> #include &l

1632: [Usaco2007 Feb]Lilypad Pond

1632: [Usaco2007 Feb]Lilypad Pond Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 404  Solved: 118[Submit][Status][Discuss] Description Farmer John 建造了一个美丽的池塘,用于让他的牛们审美和锻炼.这个长方形的池子被分割成了 M 行和 N 列( 1 ≤ M ≤ 30 ; 1 ≤ N ≤ 30 ) 正方形格子的 .某些格子上有惊人的坚固的莲花,还有一些岩石,

bzoj:1632: [Usaco2007 Feb]Lilypad Pond

Description Farmer John 建造了一个美丽的池塘,用于让他的牛们审美和锻炼.这个长方形的池子被分割成了 M 行和 N 列( 1 ≤ M ≤ 30 ; 1 ≤ N ≤ 30 ) 正方形格子的 .某些格子上有惊人的坚固的莲花,还有一些岩石,其余的只是美丽,纯净,湛蓝的水. 贝茜正在练习芭蕾舞,她从一个莲花跳跃到另一个莲花,当前位于一个莲花.她希望在莲花上一个一个的跳,目标是另一个给定莲花.她能跳既不入水,也不到一个岩石上. 令门外汉惊讶的是,贝茜的每次的跳跃像中国象棋的马一样:横

$[\ USACO\ 2007\ FEB\ ]\ Lilypad\ Pond\ (Silver)$

\(\\\) \(Description\) 一张\(N\times M\)的网格,已知起点和终点,其中有一些地方是落脚点,有一些地方是空地,还有一些地方是坏点. 现在要从起点到终点,每次移动走日字\((\)横一纵二或横二纵一\()\),其中只能经过起点.终点.落脚点. 现在可以开发任意个数的空地变为落脚点,问找到合法路径最少需要开发多少个空地,在满足第一个条件下最少移动多少步,在满足前两个条件下有多少条不同的路径. \(N,M\in [1,30]\) \(\\\) \(Solution\) 被

bzoj1698 / P1606 [USACO07FEB]白银莲花池Lilypad Pond

P1606 [USACO07FEB]白银莲花池Lilypad Pond 转化为最短路求解 放置莲花的方法如果直接算会有重复情况. 于是我们可以先预处理和已有莲花之间直接互相可达的点,将它们连边(对,忽略它们). 于是剩下的就是边权为1的边了. 酱紫我们就成功转化为了边权问题. 蓝后跑跑最短路顺便计个数就解决了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue&

BZOJ 1633: [Usaco2007 Feb]The Cow Lexicon 牛的词典

题目 1633: [Usaco2007 Feb]The Cow Lexicon 牛的词典 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 401  Solved: 216[Submit][Status] Description 没有几个人知道,奶牛有她们自己的字典,里面的有W (1 ≤ W ≤ 600)个词,每个词的长度不超过25,且由小写字母组成.她们在交流时,由于各种原因,用词总是不那么准确.比如,贝茜听到有人对她说"browndcodw"

[BZOJ1697][Usaco2007 Feb]Cow Sorting牛排序

1697: [Usaco2007 Feb]Cow Sorting牛排序 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 712  Solved: 416 [Submit][Status][Discuss] Description 农夫JOHN准备把他的 N(1 <= N <= 10,000)头牛排队以便于行动.因为脾气大的牛有可能会捣乱,JOHN想把牛按脾气的大小排序.每一头牛的脾气都是一个在1到100,000之间的整数并且没有两头牛的脾气值相同.

[BZOJ] 1631: [Usaco2007 Feb]Cow Party

1631: [Usaco2007 Feb]Cow Party Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 866  Solved: 624[Submit][Status][Discuss] Description 农场有N(1≤N≤1000)个牛棚,每个牛棚都有1只奶牛要参加在X牛棚举行的奶牛派对.共有M(1≤M≤100000)条单向路连接着牛棚,第i条踣需要Ti的时间来通过.牛们都很懒,所以不管是前去X牛棚参加派对还是返回住所,她们都采用了用时最

Bzoj 1696: [Usaco2007 Feb]Building A New Barn新牛舍 中位数,数学

1696: [Usaco2007 Feb]Building A New Barn新牛舍 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 394  Solved: 181[Submit][Status][Discuss] Description 经过多年的积蓄,农夫JOHN决定造一个新的牛舍.他知道所有N(2 <= N <= 10,000)头牛的吃草位置,所以他想把牛舍造在最方便的地方. 每一头牛吃草的位置是一个整数点(X_i, Y_i) (-10,0