洛谷P1514 引水入城【记忆化搜索+线段覆盖】

题目链接:https://www.luogu.org/problemnew/show/P1514
题目大意:n*m个城市,要求在最后一行的干旱区所有城市中建设水利设施。水利设施有两种,一种为蓄水厂,一种为输水站。第一行城市由于沿海,可以建设蓄水厂。某城市建输水站的要求是相邻(上下左右)且高度比自己高的城市建有水利设施(利用高度差输水)。如果可以全部建成,则输出最少要建几个蓄水厂。否则输出干旱区中不可能建有水利设施的城市数目。
思路:用结构体dp[x][y]表示点(x,y)可以覆盖到的范围[l,r]。在可以全部覆盖的情况下,先用dfs搜出第一行每个城市可以覆盖的区域(一定是相连的,否则不通),再用贪心解决线段覆盖问题,即可得到最小数量。用记忆化搜索,否则会T:对于已经填过的dp,直接调用,无需dfs。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn=500+5;
int dir[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
int n,m,cnt,flag,h[maxn][maxn],vis[maxn][maxn];
struct node {
    int x,y;
} p[maxn],s;
struct lnode {
    int l,r;
} dp[maxn][maxn];
bool cmp(lnode a,lnode b) {
    if(a.l==b.l) return a.r<b.r;
    else return a.l<b.l;
}
void dfs(node a) { 
	vis[a.x][a.y]=1;
    for(int i=0; i<4; i++) {
    	node tmp;
        tmp.x=a.x+dir[i][0];
        tmp.y=a.y+dir[i][1];
        if(tmp.x>0&&tmp.x<=n&&tmp.y>0&&tmp.y<=m&&h[tmp.x][tmp.y]<h[a.x][a.y]) {
            if(!vis[tmp.x][tmp.y]) //没访问过的进行dfs,否则直接更新该点dp值 
                dfs(tmp);
            dp[a.x][a.y].l=min(dp[a.x][a.y].l,dp[tmp.x][tmp.y].l);
        	dp[a.x][a.y].r=max(dp[a.x][a.y].r,dp[tmp.x][tmp.y].r);
        }
    }
}
int main() {
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            scanf("%d",&h[i][j]);
            dp[i][j].l=m+1;
            dp[i][j].r=0;
        }
    }
    for(int i=1;i<=m;i++) //dp铺好基层 
    	dp[n][i].l=dp[n][i].r=i;
    for(int i=1; i<=m; i++) {
        s.x=1,s.y=i;
        if(!vis[s.x][s.y]) dfs(s);
        //访问过的点就不用dfs了,已经更新过dp数组并且之前的一定大于等于他所能覆盖的范围 
    }
    flag=1;
    for(int i=1; i<=m; i++) {
        if(!vis[n][i]) { //判断是否可以完全覆盖 
            flag=0;
            cnt++;
        }
//		printf("%d %d\n",dp[1][i].l,dp[1][i].r);
    }
    if(!flag) {
        printf("0\n");
        printf("%d\n",cnt);
    } 
    else {
        sort(dp[1]+1,dp[1]+1+m,cmp);
        int len=0,maxl=0,st=1;
        while(len<m) { //贪心 
            for(int i=st;i<=m;i++){
                if(dp[1][i].l<=len+1&&dp[1][i].r>maxl) { 
                    maxl=dp[1][i].r;
                    st=i;
                }
            } 
            len=maxl;
            cnt++;
        }
        printf("1\n");
        printf("%d\n",cnt); 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值