洛谷P1514 引水入城
P1514 引水入城
思路:
看完这道题马上想到: 水只往低处流, 直接把每个蓄水池可以 到达的地方全部搜出来!
首先看不能覆盖最后一行的情况: 直接从第一行开始 bfs 一遍如果最后一行有走不到的, 统计一下数量直接输出.
如果可以覆盖呢? 每个蓄水池走出来的最后一行的点可能是断断续续的, 如果要 dp, 状态就会非常难设置. 如果直接状压, 数据范围 500 ......
实际上上面的情况是不会出现的.
如果一个蓄水池走出来的点 中间是有一个点断开, 那么这个点对于任意一个蓄水池来说都是走 不到的. 这会在判断无解时排除.
为什么一定走不到?
中间有个点断开了, 但是两侧都可以流到?
那么中间这个点一定会高于两侧的点, 并且也高于上面的点. 所以还会有谁会流到它呢?
有了上面这个结论, 对于每一个蓄水池, 它在最后一行所走出来 的点一定是一段连续的区间. 把这些区间拿出来, 就变成了经典的 线段覆盖问题. 由于数据范围很小, O(n2) 的做法也是可行的: 设 dp[i] 为覆盖 1 到 i 的区间所需要的最少线段数. 把每个区间按照 l, r 双关键字 排序, 枚举区间转移:
dp[Ri]=minLi<j<Ri{dp[j]}+1
最后,我们可以直接用 dfs + 记忆化找到这些区间,这部分的时间复杂度 O(nm)。
代码:
#include<bits/stdc++.h> using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') { b=-1; } c=getchar(); } while(isdigit(c)) { a=(a<<3)+(a<<1)+(c^48); c=getchar(); } return a*b; } bool flag; int n,m,cnt; int high[501][501]; bool vis[501][501]; int l[501][501]; int r[501][501]; int x[5]={0,1,-1,0,0}; int y[5]={0,0,0,1,-1}; inline void dfs(int nx,int ny) { vis[nx][ny]=1; for(int i=1;i<=4;i++) { if(nx+x[i]<1||ny+y[i]<1||nx+x[i]>n||ny+y[i]>m) { continue; } if(high[nx+x[i]][ny+y[i]]>=high[nx][ny]) { continue; } if(!vis[nx+x[i]][ny+y[i]]) { dfs(nx+x[i],ny+y[i]); } l[nx][ny]=min(l[nx][ny],l[nx+x[i]][ny+y[i]]); r[nx][ny]=max(r[nx][ny],r[nx+x[i]][ny+y[i]]); } } int main() { n=read(); m=read(); memset(l,63,sizeof(l)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { high[i][j]=read(); } } for(int i=1;i<=m;i++) { l[n][i]=r[n][i]=i; } for(int i=1;i<=m;i++) { if(!vis[1][i]) { dfs(1,i); } } for(int i=1;i<=m;i++) { if(!vis[n][i]) { flag=1; cnt++; } } if(flag) { puts("0"); printf("%d",cnt); return 0; } int left=1; while(left<=m) { int maxright=0; for(int i=1;i<=m;i++) { if(l[1][i]<=left) { maxright=max(maxright,r[1][i]); } } cnt++; left=maxright+1; } puts("1"); printf("%d",cnt); return 0; }