zoukankan      html  css  js  c++  java
  • 引水入城

    最近在搞提高组的题,这是某天早上给的T1

    T1最难还行

    原题目

    最近考试考多了就是见题打暴力,打搜索,

    然而这题真是搜索,

    但是并不能只搜索,会T,没亲测,但一定有效

    这并不是考试题,所以看看标签(理直气壮的理由)

    是BFS啊...

    那就用DFS吧

    这里的DP一开始看没有什么感觉,但是做着做着发现就是不应该有感觉,这是搜索+贪心啊

    下面开始正式题解

    首先明白一点,同一个水源可以有不同的目的地,而不同的目的地也可以有着不同的水源,由于这题要求看最少源头或是未通水的目的地数目,所以要对每个水源进行深搜,对能够通水的最后一行的格子进行标记

    在来证明一个东西:

    不存在源头(a)(b)左边,但是(a)到达的区间在(b)所到达的右边,

    就像这个图(无视那个(S_1)):

    这个图很明显存在交叉,然而如果两个水源能够到达同一格,那么这个格所能够到达的所有下面的点这两个源头一定都能达到,并不存在交叉非公共部分

    那么是否所有的搜索方式都不用网上搜呢?

    你考虑这样一种路径就好了(灵魂手绘):

    注意在进行vis数组标记时,所记录的到过的点只对本次搜索生效,也就是说,本次搜索搜到过的点待会可能别的点也要搜,并且搜到的区间也许更大,这对结构有着更大贡献,,如果因搜到过而停止搜索,会导致代码输出大部分大于答案,所以每次搜索到最后一层是需要用该点的表示列的坐标(由于横纵坐标与本题x,y在本题搞得有点昏,暂且这样称呼,有兴趣自己推吧)对该水源的左右区间端点进行更新

    然后处理出每个水源能够到的最左端与最右端,

    首先跑一遍最后一列,看有没有最后一行没有到达的格子如果有就是无解,在遍历时统计数量输出即可,

    如果全部到达,则用sort函数(按左端点)排一下,并且每次去最长的线段,就是右端点最靠后的线段,当然前提是满足左端点小于上一条线段的右端点(对于第一条线段,其左边的限制是1)

    每次将下一条线段的左端点限制更新为r+1,意义自证,

    对于每个水源的区间更新,给出以下公式:

    (l_i=min(l_i,y))

    (r_i=max(r_i,y))

    就是对于每个水源(i),用每个到达的最后一行的格子列坐标进行更新,当然要的是最左边的(l)和最右边的(r),分别用(min)函数和(max)函数取区间

    当然,初始化时要把(l)赋值成(inf)(无穷大),把(r)赋值成(-inf),来保证区间更新操作的稳定

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define R register
    const int inf=0x3f3f3f3f;
    int n,m;
    int pos[505][505];
    struct node{
    	int l,r;
    	node(){
    		l=inf;
    		r=-inf;
    	}
    }nd[505];
    int mx[6]={0,-1,0,1,0};
    int my[6]={0,0,1,0,-1};
    bool vis[505][505],final[505];
    inline void search(const int &x,const int &y,const int &p){
    	vis[x][y]=1;
    	if(x==n){
    		final[y]=1;
    		nd[p].l=min(nd[p].l,y);
    		nd[p].r=max(nd[p].r,y);
    	}
    	for(R int i=1;i<=4;i++){
    		int nx=x+mx[i];
    		int ny=y+my[i];
    		if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&(!vis[nx][ny])&&(pos[x][y]>pos[nx][ny]))
    			search(nx,ny,p);
    	}
    }
    inline bool cmp(const node &a,const node &b){
    	if(a.l!=b.l) return a.l<b.l;
    	return a.r<b.r;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(R int i=1;i<=n;i++)
    		for(R int j=1;j<=m;j++)
    			scanf("%d",&pos[i][j]);
    	for(R int i=1;i<=m;i++)
    		if(pos[1][i-1]<=pos[1][i]&&pos[1][i]>=pos[1][i+1]){
    			memset(vis,0,sizeof vis);
    			search(1,i,i);
    		}
    	int tot=0;
    	for(R int i=1;i<=m;i++)
    		if(!final[i]) tot++;
    	if(tot){
    		printf("0
    %d",tot);
    		return 0;
    	}
    	int num=0;
    	for(R int i=1;i<=m;i++)
    		if(nd[i].l!=inf&&nd[i].r!=-inf){
    			nd[++num].l=nd[i].l;
    			nd[num].r=nd[i].r;
    		}
    	sort(nd+1,nd+1+num,cmp);
    	for(R int l=1,r=0,j=1;l<=m;l=r+1,r=0,tot++){
    		while(nd[j].l<=l){
    			r=max(r,nd[j].r);
    			j++;
    		}
    	}
    	printf("1
    %d",tot);
    	return 0;
    }
    
  • 相关阅读:
    OCP-1Z0-051-V9.02-162题
    OCP-1Z0-051-V9.02-161题
    OCP-1Z0-051-V9.02-160题
    Matlab中矩阵的分解
    OCP-1Z0-051-V9.02-158题
    OCP-1Z0-051-V9.02-157题
    Matlab中特殊的矩阵函数
    求Matlab中矩阵的秩和迹
    Matlab中的条件数
    在android里使用boost c++
  • 原文地址:https://www.cnblogs.com/648-233/p/11166026.html
Copyright © 2011-2022 走看看