洛谷 P1514 引水入城 题解

这道题经常是作为一个搜索题出现的,但是实际上它还用dp或贪心处理搜索后的结果。

来看这道题,题意很好理解,第一问也很好做(从每个第一行的点开始搜,判断整个第一行在第N行上覆盖的点数量是否等于M就好了)

然而,想到这里还不足以解决第二问。

想要解决第二问首先需要明确一个引理:第一行某个点在第N行上覆盖的点一定是一个区间(这个自己推或看洛谷题解里的证明,实际并不是很难想)

到了这里,问题就转化为了用最少的子区间覆盖全区间的问题。

这个问题有两种方法:dp和贪心,这里我选择的是贪心。

贪心策略:对于全区间的某个点,包含该点且右端点尽量大的子区间是合法且最优的。

注:洛谷上需要一个小优化:如果第一行上的某个点x可以从其他第一行上的点y到达,那么不需要对x点进行搜索(因为路径x集合真包含于路径y集合),代码中mark数组就是完成这个优化的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define maxn 510
#define rep(i,s,e) for(register int i=s;i<=e;++i)
#define dwn(i,s,e) for(register int i=s;i>=e;--i)
using namespace std;
inline int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
    return f*x;
}
inline void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
int n,m; 
int map[maxn][maxn];
int book[maxn][maxn];
int mark[maxn];
int last_row[maxn];
struct node
{
    int l,r;
}s[maxn];
int dx[5]={0,1,-1,0,0};
int dy[5]={0,0,0,1,-1};
bool cmp(node x,node y)
{
    return x.r>y.r;
}
void dfs(int x,int y)
{
    if(x==n) last_row[y]++;
    if(x==1) mark[y]=1;
    book[x][y]=1;
    rep(i,1,4)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&map[xx][yy]<map[x][y]&&book[xx][yy]==0) 
            dfs(xx,yy);
    }
}
signed main()
{
    n=read();m=read();
    rep(i,1,n)
        rep(j,1,m)
            map[i][j]=read();
    rep(i,1,m)
    {
        if(mark[i]==1) continue;
        memset(book,0,sizeof(book));
        dfs(1,i);
        rep(j,1,m)
        {
            if((book[n][j]!=0&&book[n][j-1]==0)||(book[n][j]!=0&&j==1)) s[i].l=j;
            if(book[n][j]==0&&book[n][j-1]!=0) s[i].r=j-1;
            else if(j==m&&book[n][j]!=0) s[i].r=j;
        }
    }
    int cnt=0;
    rep(i,1,m) 
    {
        if(last_row[i]!=0) ++cnt;
    }
    if(cnt==m) 
    {
        cout<<1<<endl;
        sort(s+1,s+m+1,cmp); 
        int ans=0;
        rep(i,1,m)
            rep(j,1,m)
                if(i>=s[j].l&&i<=s[j].r) 
                {
                    i=s[j].r;
                    ++ans;
                    break;
                }
        cout<<ans;
    }
    else 
    {
        cout<<0<<endl<<m-cnt;
        return 0;
    }
    return 0;
}

 

posted @ 2020-09-04 22:23  handsome_zyc  阅读(275)  评论(2编辑  收藏  举报