洛谷 2472 [SCOI2007]蜥蜴 题解

探讨了一个关于蜥蜴从石柱上跳跃并尝试逃离矩阵的问题,利用网络流算法解决,介绍了算法的具体实现和代码细节。

博客观赏效果更佳

题意简述

有一个 n × m n\times m n×m 的矩阵,其中一些位置上有蜥蜴。每个位置上有一个石柱,给你他们初始的高度 a i , j a_{i,j} ai,j。一个蜥蜴可以从一个石柱,跳到直线距离 ≤ k \le k k 的另一个石柱上。当一只蜥蜴从一个石柱上离开的时候,这个石柱的高度就会减少 1 1 1 。如果蜥蜴跳到了矩阵外面,就是逃离了。请你求最少有多少只不能逃离(其实就是最多能逃离几只)。

1 < = n , m < = 20 1<=n,m<=20 1<=n,m<=20 k < = 4 k<=4 k<=4

【附】直线距离:从 ( x 1 , y 1 ) (x1,y1) (x1,y1) ( x 2 , y 2 ) (x2,y2) (x2,y2) 的直线距离为: ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 \sqrt{(x1-x2)^2+(y1-y2)^2} (x1x2)2+(y1y2)2

思路框架

显然建网络流。首先我们把矩阵展开,就是把 ( i , j ) (i,j) (i,j) 位置的点编号为 ( i − 1 ) m + j (i-1)m+j (i1)m+j

每个石柱上只能通过定量的蜥蜴,这显然是一个点限流。套路拆点,每个位置变成 I n ( i , j ) In(i,j) In(i,j) O u t ( i , j ) Out(i,j) Out(i,j)。设源点为 S S S ,汇点为 T T T

对于每个点 ( i , j ) (i,j) (i,j)

I n ( i , j ) → a i , j O u t ( i , j ) In(i,j) \xrightarrow[a_{i,j}]{} Out(i,j) In(i,j) ai,jOut(i,j) (一个石柱上只能通过 a i , j a_{i,j} ai,j 个蜥蜴)}
S → 1 I n ( i , j ) S \xrightarrow[1]{} In(i,j) S 1In(i,j) (一个石柱上只能有一个蜥蜴)
O u t ( i , j ) → i n f T Out(i,j) \xrightarrow[inf]{} T Out(i,j) infT (跳出终点就随便了)

对于点 ( i , j ) (i,j) (i,j) 和点 ( u , v ) (u,v) (u,v) 满足 ( i , j ) (i,j) (i,j) ( u , v ) (u,v) (u,v) 直线距离 ≤ k \le k k
O u t ( i , j ) → i n f I n ( u , v ) Out(i,j) \xrightarrow[inf]{} In(u,v) Out(i,j) infIn(u,v) (点之间跳是不限的)
O u t ( u , v ) → i n f I n ( i , j ) Out(u,v) \xrightarrow[inf]{} In(i,j) Out(u,v) infIn(i,j)

这样跑一个最大流即珂。

Q:为什么点到点,点到 T T T 之间都是 i n f inf inf 的边呢?怎么就“随便”了?
A:其实并不是随便,我们知道,网络流上一个流能流过的值是其路径上的最小值。那么既然 I n ( i , j ) In(i,j) In(i,j) O u t ( i , j ) Out(i,j) Out(i,j) 的时候,已经限过了一次 a i , j a_{i,j} ai,j ,那我们从 O u t ( i , j ) Out(i,j) Out(i,j) I n ( u , v ) In(u,v) In(u,v) 的时候,再限一次也没有必要了。当然,你如果实在要限这条边流量为 a i , j a_{i,j} ai,j,没有任何问题。

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
	#define N 1333
	#define INF 0x3f3f3f3f
	#define F(i,l,r) for(int i=l;i<=r;++i)
	#define D(i,r,l) for(int i=r;i>=l;--i)
	#define Fs(i,l,r,c) for(int i=l;i<=r;c)
	#define Ds(i,r,l,c) for(int i=r;i>=l;c)
	#define MEM(x,a) memset(x,a,sizeof(x))
	#define FK(x) MEM(x,0)
	#define p_b push_back
	#define sz(a) ((int)a.size())
	#define iter(a,p) (a.begin()+p)
	void R1(int &x)
	{
	    x=0;char c=getchar();int f=1;
	    while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
	    while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	    x=(f==1)?x:-x;
	}
	void Rd(int cnt,...)
	{
	    va_list args;
	    va_start(args,cnt);
	    F(i,1,cnt) 
	    {
	        int* x=va_arg(args,int*);R1(*x);
	    }
	    va_end(args);
	}
	class Graph //这些是板子
	{
    public:
        int EdgeCount;
        int head[N];
        struct Edge
        {
            int To,Label;
            int Next;
        }Ed[200100];
        void clear()
        {
            MEM(head,-1);
            MEM(Ed,-1);
            EdgeCount=-1;
        }
        void AddEdge(int u,int v,int w)
        {
            ++EdgeCount;
            Ed[EdgeCount]=(Edge){v,w,head[u]};
            head[u]=EdgeCount;
        }
        void AddFlow(int u,int v,int w)
        {
            AddEdge(u,v,w);
            AddEdge(v,u,0);
        }

        int Source,Sink;
        int deep[N];
        queue<int>Q,EmptyQ;
        bool BFS()
        {
            Q=EmptyQ;
            FK(deep);

            Q.push(Source);
            deep[Source]=1;
            do
            {
                int u=Q.front();Q.pop();
                for(int i=head[u];~i;i=Ed[i].Next)
                {
                    int v=Ed[i].To;
                    if (deep[v]==0 and Ed[i].Label>0)
                    {
                        deep[v]=deep[u]+1;
                        Q.push(v);
                    }
                }
            }while(!Q.empty());

            if (deep[Sink]==0) return 0;
            return 1;
        }
        int DFS(int u,int MinFlow)
        {
            if (u==Sink) return MinFlow;
            for(int i=head[u];~i;i=Ed[i].Next)
            {
                int v=Ed[i].To;
                if (deep[v]==deep[u]+1 and Ed[i].Label!=0)
                {
                    int d=DFS(v,min(MinFlow,Ed[i].Label));
                    if (d>0)
                    {
                        Ed[i].Label-=d;
                        Ed[i^1].Label+=d;
                        return d;
                    }
                }
            }
            return 0;
        }
        int Dinic()
        {
            int ans=0;
            while(BFS())
            {
                int d;
                while(d=DFS(Source,0x3f3f3f3f))
                {
                    ans+=d;
                }
            }
            return ans;
        }
	}Nt;
	#define S Nt.Source
	#define T Nt.Sink
	#define id(i,j,x) (x*n*m+(i-1)*m+j)
	//(x,y,0): In
	//(x,y,1): Out

	int n,m,k; int cnt;
	int a[N][N]; char t[N];
	void Input()
	{
		Rd(3,&n,&m,&k);
		F(i,1,n) F(j,1,m) scanf("%1d",&a[i][j]);
		Nt.clear();
		S=2*n*m+1,T=2*n*m+2;
		F(i,1,n)
		{
			scanf("%s",t+1);
			F(j,1,m) if (t[j]=='L' and a[i][j]>0) 
           //如果一开始石柱就没了,我们就忽略这个蜥蜴
			{
				++cnt;
				Nt.AddFlow(S,id(i,j,0),1); 
			}
		}
	}

	void Soviet()
	{
		F(i,1,n) F(j,1,m)     Nt.AddFlow(id(i,j,0),id(i,j,1),a[i][j]);
        //建In(i,j)到Out(i,j)的边
		F(i,1,n) F(j,1,m) if (i-k<1 or j-k<1 or i+k>n or j+k>m)
		{
			Nt.AddFlow(id(i,j,1),T,INF);
           //建立到终点的边
		}
		F(i,1,n) F(j,1,m) F(u,max(i-k,1),min(i+k,n)) F(v,max(j-k,1),min(j+k,m)) if (a[i][j] and a[u][v])
		{
			if (i==u and v==j) continue;
			if ((i-u)*(i-u)+(j-v)*(j-v)<=k*k)
           //直线距离<=k
			{
				Nt.AddFlow(id(i,j,1),id(u,v,0),INF);
				Nt.AddFlow(id(u,v,1),id(i,j,0),INF);
              //建边
			}
		}
		printf("%d\n",cnt-Nt.Dinic());
       //蜥蜴的数量-最大的能逃脱的数量,就是最少的不能逃脱的数量
	}

	#define Flan void
	Flan IsMyWife()
	{
		Input();
		Soviet();
	}
}
int main()
{
	Flandre_Scarlet::IsMyWife();
	getchar();getchar();
	return 0;
}
内容概要:本文系统阐述了Java Persistence API(JPA)的核心概念、技术架构、核心组件及实践应用,重点介绍了JPA作为Java官方定义的对象关系映射(ORM)规范,如何通过实体类、EntityManager、JPQL和persistence.xml配置文件实现Java对象与数据库表之间的映射与操作。文章详细说明了JPA解决的传统JDBC开发痛点,如代码冗余、对象映射繁琐、跨数据库兼容性差等问题,并解析了JPA与Hibernate、EclipseLink等实现框架的关系。同时提供了基于Hibernate和MySQL的完整实践案例,涵盖Maven依赖配置、实体类定义、CRUD操作实现等关键步骤,并列举了常用JPA注解及其用途。最后总结了JPA的标准化优势、开发效率提升能力及在Spring生态中的延伸应用。 适合人群:具备一定Java基础,熟悉基本数据库操作,工作1-3年的后端开发人员或正在学习ORM技术的中级开发者。 使用场景及目标:①理解JPA作为ORM规范的核心原理与组件协作机制;②掌握基于JPA+Hibernate进行数据库操作的开发流程;③为技术选型、团队培训或向Spring Data JPA过渡提供理论与实践基础。 阅读建议:此资源以理论结合实践的方式讲解JPA,建议读者在学习过程中同步搭建环境,动手实现文中示例代码,重点关注EntityManager的使用、JPQL语法特点以及注解配置规则,从而深入理解JPA的设计思想与工程价值。
### 解题思路 洛谷 P4160 [SCOI2009] 生日快乐 这道题的核心在于递归与分治策略。题目要求将一个矩形蛋糕切成若干块,使得每一块的长宽比的最大值最小。由于每一块的长宽比是独立的,因此可以通过递归的方法,将问题分解为子问题来求解。 #### 核心思路: 1. **递归切分**:每次将蛋糕分成两部分,并递归地对这两部分进行同样的操作,直到只剩一块为止。 2. **枚举切分方式**:对于每一层递归,需要枚举所有可能的切分方式(横向或纵向),以及每一块的大小比例。 3. **取最大值与最小值**:每一步递归中,选择切分方式使得两部分的最大长宽比尽可能小。 #### 关键点: - **长宽比处理**:为了保证长宽比的计算准确,需要确保长边在分子,短边在分母。 - **切分方式枚举**:枚举所有可能的切分比例,确保没有遗漏。 - **递归终止条件**:当只剩一块时,直接返回当前长宽比。 ### 代码示例 以下是一个完整的代码实现,展示了如何通过递归方法解决这个问题: ```cpp #include <iostream> #include <cstdio> #include <algorithm> using namespace std; int x, y, n; // 计算最大公约数 int gcd(int x, int y) { if (y == 0) return x; return gcd(y, x % y); } // 递归函数,用于计算最小的长宽比 double qie(int x, int y, int n) { if (x < y) swap(x, y); // 保证x是较长边 int g = gcd(x, y); if (g != 1) { x /= g; y /= g; } if (n == 1) return static_cast<double>(x) / y; // 终止条件 double ans = 10000000; for (int i = 1; i < n; ++i) { // 横向切分 ans = min(ans, max(qie(x * i, y * n, i), qie(x * (n - i), y * n, n - i))); // 纵向切分 ans = min(ans, max(qie(x * n, y * i, i), qie(x * n, y * (n - i), n - i))); } return ans; } int main() { scanf("%d%d%d", &x, &y, &n); printf("%.6lf\n", qie(x, y, n)); return 0; } ``` ### 代码解析 1. **gcd函数**:用于化简长宽比,避免浮点数计算误差。 2. **qie函数**: - 首先交换长宽,确保长边在前。 - 化简长宽比的分数,避免重复计算。 - 递归终止条件:当只剩一块时,返回长宽比。 - 枚举所有可能的切分方式,取最小的长宽比。 3. **main函数**:读取输入并调用递归函数,输出结果。 ### 复杂度分析 - **时间复杂度**:由于每次递归会枚举所有可能的切分方式,时间复杂度为指数级,但由于数据范围较小,可以通过递归直接解决。 - **空间复杂度**:递归深度由切分次数决定,空间复杂度较低。 ### 总结 这道题通过递归的方式,将大问题分解为子问题,结合枚举所有可能的切分方式,最终找到最优解。递归与分治策略是解决此类问题的核心思想。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值