[BFS+贪心]【NOIP2010T4】引水入城 题解

本文介绍了一种解决图论中最少线段覆盖问题的方法,通过将问题转化为二维网格上的节点传递问题,并利用广度优先搜索算法进行求解。讨论了如何从第一行开始传递到最后一行,并确保覆盖所有必要的节点。

(传送门)

解题报告

蒟蒻A水题中……

这道题首先将所有第一行往下扩展,看最后一行能否全部传到来判断无解。

如果有解,那么可以证明,每个第一行的节点所能传到的最后一行节点一定是一个区间。

然后就是一个最少线段覆盖问题了。蒟蒻都忘了板子……

复杂度:
时间: O(nm)
空间: O(nm)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int flg[4][2]={{1,0},{0,-1},{-1,0},{0,1}};
int n,m,k,mp[505][505],ans;
bool vs[505][505];
struct data{
    int x,y;
    data (int x=0,int y=0):x(x),y(y){}
    bool operator < (const data b)const{
        return x<b.x;
    }
}que[250005],b[505];
inline char nc(){
    static char buf[100000],*pa=buf,*pb=buf;
    return pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++;
}
inline void readi(int &x){
    x=0; char ch=nc();
    while ('0'>ch||ch>'9') ch=nc();
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=nc();}
}
bool _check(int x,int y,int xx,int yy){
    if (x<1||x>n||y<1||y>m) return 0;
    if (mp[x][y]>=mp[xx][yy]) return 0;
    if (vs[x][y]) return 0;
    return 1;
}
void _bfsa(){
    memset(vs,0,sizeof(vs)); int hed=0,til=0;
    for (int i=1;i<=m;i++) {que[++til]=data(1,i); vs[1][i]=1;}
    while (hed!=til){
        int x=que[++hed].x,y=que[hed].y;
        for (int i=0;i<4;i++)
            if (_check(x+flg[i][0],y+flg[i][1],x,y)){
                int tx=x+flg[i][0],ty=y+flg[i][1];
                que[++til]=data(tx,ty); vs[tx][ty]=1;
            }
    }
}
void _bfsb(int sy){
    memset(vs,0,sizeof(vs));
    int hed=0,til=1; que[1]=data(1,sy); vs[1][sy]=1;
    while (hed!=til){
        int x=que[++hed].x,y=que[hed].y;
        for (int i=0;i<4;i++)
            if (_check(x+flg[i][0],y+flg[i][1],x,y)){
                int tx=x+flg[i][0],ty=y+flg[i][1];
                que[++til]=data(tx,ty); vs[tx][ty]=1;
            }
    }
    int i,L=0,R;
    for (i=1;i<=m+1;i++) if (vs[n][i]){L=i; break;}
    if (!L) return;
    for (;i<=m+1;i++) if (!vs[n][i]){R=i-1;break;}
    b[++k]=data(L,R);
}
int main() 
{
    freopen("flow.in","r",stdin);
    freopen("flow.out","w",stdout);
    readi(n); readi(m); k=ans=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) readi(mp[i][j]);
    _bfsa();
    for (int j=1;j<=m;j++) ans+=vs[n][j]^1;
    if (ans) {printf("0\n%d",ans); return 0;}
    for (int j=1;j<=m;j++) _bfsb(j);
    sort(b+1,b+k+1); ans=0;
    for (int i=1,lst=1;lst<=m&&i<=k;){
        int t=0; for (;b[i].x<=lst&&i<=k;i++) t=max(t,b[i].y);
        lst=t+1; ans++;
    }
    printf("1\n%d",ans);
    return 0;
}
### 解题思路 #### 问题分析 题目要求解决一个城市矩阵中从湖泊一侧引水到沙漠一侧的问题。矩阵中每个城市都有一个海拔高度,水可以从高处流向低处。蓄水厂只能建在第一行(靠近湖泊的一侧),而输水站可以通过高度差从高处向低处输送水。目标是判断是否能够覆盖最后一行的所有城市,如果不能,则输出无法覆盖的城市数量;如果可以,则计算最少需要建造多少个蓄水厂。 #### 解题方法 1. **BFS(广度优先搜索)**: - 由于水只能从高处流向低处,可以使用 BFS 来模拟水的流动过程。 - 从第一行的所有城市开始,逐层向下扩展,记录哪些城市可以被覆盖。 2. **动态规划(DP)**: - 在 BFS 确定哪些城市可以被覆盖后,使用动态规划来确定最少需要建造多少个蓄水厂。 - 动态规划的状态可以表示为:`dp[i]` 表示前 `i` 列中可以覆盖的最后一行的最少蓄水厂数量。 3. **贪心策略**: - 在动态规划的基础上,结合贪心策略,选择能够覆盖最多未覆盖区域的蓄水厂位置,从而减少蓄水厂的数量。 #### 具体实现 1. **BFS 实现**: - 初始化一个队列,将第一行的所有城市加入队列。 - 使用一个二维数组记录哪些城市已经被访问。 - 从队列中取出一个城市,检查其四个相邻城市(上下左右),如果相邻城市的海拔较低且未被访问,则将其加入队列。 2. **动态规划与贪心**: - 使用一个数组 `dp` 来记录每一列的最少蓄水厂数量。 - 遍历每一列,更新 `dp` 数组,确保每一列的蓄水厂能够覆盖最后一行的所有城市。 ```python from collections import deque def solve_water_distribution(n, m, grid): visited = [[False] * m for _ in range(n)] queue = deque() # 初始化队列,第一行的所有城市 for j in range(m): if grid[0][j] > 0: queue.append((0, j)) visited[0][j] = True # 四个方向:上下左右 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # BFS while queue: x, y = queue.popleft() for dx, dy in directions: nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m: if not visited[nx][ny] and grid[nx][ny] < grid[x][y]: visited[nx][ny] = True queue.append((nx, ny)) # 检查最后一行是否全部覆盖 covered = sum(visited[n-1][j] for j in range(m)) if covered == m: # 使用动态规划计算最少蓄水厂数量 dp = [float('inf')] * (m + 1) dp[0] = 0 for i in range(1, m + 1): for j in range(i): if can_cover(j, i, visited): dp[i] = min(dp[i], dp[j] + 1) return dp[m] else: return m - covered def can_cover(start, end, visited): # 检查从 start 到 end 是否可以覆盖最后一行的所有城市 for j in range(start, end): if not visited[-1][j]: return False return True ``` #### 复杂度分析 - **时间复杂度**:BFS 的时间复杂度为 O(N*M),动态规划的时间复杂度为 O(M^2),其中 N 和 M 分别为矩阵的行数和列数。 - **空间复杂度**:主要使用了两个二维数组 `visited` 和 `dp`,空间复杂度为 O(N*M)。 ### 相关问题 1. 如何优化 BFS 的性能以减少内存占用? 2. 在动态规划中,如何处理边界条件以确保结果的正确性? 3. 如果矩阵的规模很大,如何调整算法以适应大规模数据? 4. 除了 BFS 和动态规划,还有哪些算法可以用于解决类似问题? 5. 如何验证 BFS 和动态规划的正确性?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值