• [校内集训] 氵


    题意

    给一张网格图,每个格子有高度,假设边界之外的区域无限大且高度为0,向整个图灌氵,问到最后每个格子上氵柱的高度(氵往低处流)

    思路

    贪心:一个点可以存的氵量等于它到网格图外面的一条路径上最高的格子减去自己的高度

    限制:上述路径最大值中应该选取最小的,因为如果选了一条最大值大的路径,氵就会从另一条最大值较小的路径流走

    将相邻点之间连边,边权为两个格子高度的较大值,边界格子向根节点连边,边权为(max(h,0)),对于限制,跑出原图的最小生成树(即保证两点之间连通性的最短路径),对于贪心,从根出发到每个点即可得到上面说的最大值,(ans[i][j]=maxx-h[i][j])

    Code

    #include<bits/stdc++.h>
    #define N 305
    #define Min(x,y) ((x)<(y) ? (x):(y))
    using namespace std;
    const int INF = 1000000000;
    int n,m,s,c,fa[N*N];
    int a[N][N],ans[N][N];
    int dox[4]={0,-1,0,1},doy[4]={1,0,-1,0};
    struct E {int u,v,w;} e[N*N*4];
    struct Edge
    {
    	int next,to,dis;
    }edge[N*N*2];int head[N*N],cnt;
    void add_edge(int from,int to,int dis)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].dis=dis;
    	head[from]=cnt;
    }
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    int find(int x) {return x==fa[x] ? fa[x] : fa[x] = find(fa[x]);} 
    int getid(int x,int y) {return (x-1)*m+y;}
    bool cmp(E a,E b) {return a.w<b.w;}
    void kruskal()
    {
    	for(int i=1;i<=n*m+1;++i) fa[i]=i;
    	sort(e+1,e+c+1,cmp);
    	int p=0;
    	for(int i=1;i<=c;++i)
    	{
    		int fx=find(e[i].u),fy=find(e[i].v);
    		if(fx!=fy)
    		{
    			fa[fx]=fy;
    			add_edge(e[i].u,e[i].v,e[i].w);
    			add_edge(e[i].v,e[i].u,e[i].w); 
    			if(++p==n*m) break;
    		}
    	}
    }
    void dfs(int rt,int fa,int maxx)
    {
    	ans[(rt-1)/m+1][(rt-1)%m+1]=maxx-a[(rt-1)/m+1][(rt-1)%m+1];
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa) continue;
    		dfs(v,rt,max(maxx,edge[i].dis));
    	}
    }
    int main()
    {
    	freopen("water.in","r",stdin);
    	freopen("water.out","w",stdout);
    	read(n);read(m);
    	s=n*m+1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			read(a[i][j]);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			for(int k=0;k<4;++k)
    			{
    				int dx=dox[k]+i,dy=doy[k]+j;
    				if(dx<1||dy<1||dx>n||dy>m) e[++c]=(E){s,getid(i,j),max(a[i][j],0)};
    				else e[++c]=(E){getid(i,j),getid(dx,dy),max(a[i][j],a[dx][dy])};
    			}
    	kruskal();
    	dfs(s,0,-INF);
    	for(int i=1;i<=n;++i)
    	{
    		for(int j=1;j<=m;++j) printf("%d ",ans[i][j]);
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    事务
    数据库三大范式
    jdbc
    Jdbc事务
    Android 学习笔记2开发软件的常识&搭建开发环境(windows7)
    2011年起航
    Android 学习笔记3Android 工程目录介绍&程序执行过程
    我的第一篇日志基于AT89S52的单片机的LED点阵显示屏的设计
    算法基础之快速排序
    LeetCode88.合并两个有序数组
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11435029.html
Copyright © 2020-2023  润新知