洛谷 P1514 引水入城
题目传送门
dfs+性质题,最直接的想法,求出第一行的每个点可以覆盖最后一行哪些点,然后做处理.但是我们求出来的有可能有一些是不连续的区间,怎么办?(想了好久),其实要用到本题的一个性质,如果最后一行的某个点的左右两个点都被第一行的同一个点覆盖而这个点没有被覆盖,那就说明第一行输出是0,简单求出不能被覆盖的点数就行.
剩下的情况就是区间覆盖问题了,要注意更新边界的细节(调了好久).
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,a[501][501],tot,ans;
bool vis[501][501],fla[501],k[501][501],vv[501][501];
int xx[] = {-1,0,1,0};
int yy[] = {0,-1,0,1};
struct kkk {
int x,y,z,vis,l,r;
}e[501],sum[501];
inline bool cmp(kkk s,kkk d) {
if(s.z == d.z) return s.y < d.y;
return s.z > d.z;
}
inline void dfs(int x,int y,int id) {
int x1,y1;
for(int i = 0;i <= 3; i++) {
x1 = x + xx[i];
y1 = y + yy[i];
if(x1 > n || x1 <= 0 || y1 > m || y1 <= 0) continue;
if(a[x1][y1] >= a[x][y]) continue;
if(vis[x1][y1]) continue;
vis[x1][y1] = 1;
vv[x1][y1] = 1;
if(x1 == n) {
e[id].l = min(e[id].l,y1);
e[id].r = max(e[id].r,y1);
k[id][y1] = 1;
}
dfs(x1,y1,id);
}
}
inline bool dp(kkk s,kkk d) {
return s.l < d.l;
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 1;i <= n; i++)
for(int j = 1;j <= m; j++) {
scanf("%d",&a[i][j]);
if(1 == i) {
e[j].l = 600;
if(n == 1) e[j].l = j,e[j].r = j;
e[j].x = i;
e[j].y = j;
e[j].z = a[i][j];
e[j].vis = 1;
}
}
sort(e+1,e+m+1,cmp);
for(int i = 1;i <= m; i++) {
if(fla[e[i].y]) continue;
fla[e[i].y] = 1;
vv[e[i].x][e[i].y] = 1;
memset(vis,0,sizeof(vis));
dfs(e[i].x,e[i].y,i);
for(int j = e[i].y - 1;j >= 1; j--) {
if(a[1][j] >= a[1][j+1]) break;
fla[j] = 1;
}
for(int j = e[i].y + 1;j <= m; j++) {
if(a[1][j] >= a[1][j-1]) break;
fla[j] = 1;
}
sum[++tot] = e[i];
}
for(int i = 1;i <= m; i++)
if(!vv[n][i])
ans++;
if(ans != 0) {
printf("0\n%d",ans);
return 0;
}
sort(sum+1,sum+1+tot,dp);
int R = 1,p = 1,rr = 0,ans;
for(p;p <= tot; p++) {
if(R > m) break;
if(sum[p].l > R) {
ans++;
R = rr + 1;//如果两个区间是[1,2][3,4]那这两个区间的并也合法
}
rr = max(rr,sum[p].r);
}
printf("1\n");
if(R <= m) ans++;//因为R=rr+1,所以R==m时实际上并没有取到m
printf("%d",ans);
return 0;
}