https://www.luogu.org/problemnew/show/P1514
题目如上
非常非常巧妙的一道题目;
1.先思考,要求的是什么?
如题目所说,目的是要把第n排全部建上那个什么水利工程,全建上,也就是全部覆盖!再考虑起点,也就是河岸,动动笔,也不难发现,某些节点可以拓展到终点的一片区域,
如样例一中9可以拓展到的点如图所示,
绿色的框则表示9,也就是入口第一列的这个点所能到达的出口范围,这里叫做他能管辖的范围;
理解到这一步,应该很明显了,要求覆盖所有终点所需要的起点最少,故覆盖完终点所需要的管辖区间最少。
over 完美的变成了区间覆盖问题!
不了解区间覆盖问题的可以参考
https://blog.csdn.net/karry_zzj/article/details/70886931
2.如何求解??
这里用的贪心,但我用的动态规划;
设f[i][j]为覆盖完区间i–j所需要最少的起点数,dp[i]表示覆盖完1–i所需要最少的起点数,故dp[m]为最终答案;
每个起点所管辖的区域如何求呢?
非常明显,搜索大法—BFS!!你想非主流用DFS也没人管你。。。
每个第一行的点开始宽搜,搜到最后一行也就是终点更新管辖区域。
那么问题也就迎刃而解了NICE!!!IG,NB!!!!
代码注释也不少,应该较好理解
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,dp[510],f[510][510],e[510][510],out[510],vis[510][510],cx[5]={0,-1,1,0,0},cy[5]={0,0,0,-1,1};
struct IN{// e存地图,out[i]表示最后一行第i列能否被覆盖到,vis用来BFS判重。
int l,r,v;
}I[510];//第i个起点的管辖区域l--r,v表示该七点是否被其他起点所搜到,如果被其他起点能到达,该起点就没暖用了。。
struct Node{int x,y;};
void BFS(int sy){
memset(vis,0,sizeof(vis));
queue<Node> q;
if(n==1) out[sy]=1,I[sy].l=I[sy].r=sy;//特别注意输入只有一行的情况,起点也是终点
q.push((Node){1,sy});
while(!q.empty()){
Node t=q.front(); q.pop();
for(int i=1;i<=4;i++){
int xx=t.x+cx[i],yy=t.y+cy[i];
if(e[xx][yy]<e[t.x][t.y]){//可以拓展
if(xx==1) I[yy].v=0;//第yy个节点可以被别的拓展到,没p用了byebye!!
if(xx==n){
out[yy]=1;
if(yy<I[sy].l) I[sy].l=yy;
if(yy>I[sy].r) I[sy].r=yy;
}//更新管辖区域
if(!vis[xx][yy]) q.push((Node){xx,yy}),vis[xx][yy]=1;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
memset(e,0x7f,sizeof(e));//初始化防止边界问题
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&e[i][j]);
for(int i=1;i<=m;i++) I[i].v=1,I[i].l=m+1,I[i].r=0;//管辖区域的初始化,因为要逐渐扩大,右端点要靠最左,同理......
for(int i=1;i<=m;i++) if(I[i].v) BFS(i);//BFS每一个节点
int sum=0;
for(int i=1;i<=m;i++) if(!out[i]) sum++;
if(sum){ printf("%d\n%d\n",0,sum);return 0;}//判断特例,不可能覆盖完
memset(dp,0x7f,sizeof(dp));
memset(f,0x7f,sizeof(f));
for(int k=1;k<=m;k++){
for(int i=I[k].l;i<=I[k].r;i++){
for(int j=i;j<=I[k].r;j++){
if(i==1) dp[j]=1;
f[i][j]=1;
}
}
}//初始化
for(int i=1;i<=m;i++) for(int j=1;j<i;j++) dp[i]=min(dp[i],dp[j]+f[j+1][i]);//dp开始!
printf("%d\n%d\n",1,dp[m]);//over!
return 0;//IG NB!!!
}
就喜欢短小精悍的代码,关注一下谢谢了!!!