【LuoguP2598 [ZJOI2009]】狼和羊的故事

本博客介绍了一个有趣的问题:如何通过构建最短的篱笆将羊和狼隔离开来,确保它们不会侵犯对方的领地。采用图论中的最大流算法解决此问题,并给出了具体的实现代码。

题目链接

题目描述

“狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! Orez的羊狼圈可以看作一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是Drake很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以Orez决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。 通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。 Orez想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

题解

上面题目说了一大堆,简单来说就是让你将一些格子的边界用篱笆围起来,使狼和羊不能走到各自对方的领地。(注意有空地!!!)

新建源点汇点 ,源点向所有羊连容量为INF的边,所有狼向汇点连容量为INF的边(可反)。其他点向周围的点连容量为1的边,但要注意流向,狼只连入边,羊只连出边,这样最大流(最小割更形象)即为最少要修的篱笆数。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
const int N=10100;
const int M=120000;
const int INF=2147483647;
struct edge{
    int to;int next;int cap;
}a[M];
int cur[N];
int head[N];
int dis[N];
#define Get(a,b) (((a)-1)*(m)+(b))
int n,m;
int cnt=0;
int map[103][103];
int tot;
inline void add(int x,int y,int z)
{
    a[cnt]=(edge){y,head[x],z};
    head[x]=cnt++;
}
queue<int> Q;
inline void clear(queue<int>&Q)
{
    queue<int> P;swap(P,Q);
}
inline bool bfs()
{
    clear(Q);
    Set(dis,-1);dis[0]=0;
    Q.push(0);
    while(!Q.empty())
    {
        register int u=Q.front();Q.pop();
        for(register int v,i=head[u];i!=-1;i=a[i].next)
        {
            v=a[i].to;
            if(a[i].cap==0||dis[v]!=-1) continue;
            dis[v]=dis[u]+1;
            if(v==tot) return 1;
            Q.push(v);
        }
    }
    return (dis[tot]!=-1);
}
inline int dfs(int u,int flow)
{
    if(u==tot) return flow;
    register int delta=flow;
    for(register int v,&i=cur[u];i!=-1;i=a[i].next)
    {
        v=a[i].to;
        if(a[i].cap==0||dis[v]!=dis[u]+1) continue;
        register int d=dfs(v,min(delta,a[i].cap));
        delta-=d;a[i].cap-=d;a[i^1].cap+=d;
        if(d==0) dis[v]=0;
        if(delta==0) break;
    }
    return flow-delta;
}
inline void Dinic()
{
    register int max_flow=0;
    while(bfs()) Copy(cur,head),max_flow+=dfs(0,INF);
    printf("%d\n",max_flow);
}
int main()
{
    Set(head,-1);
    n=read();m=read();tot=n*m+1;
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=m;j++)
        {
            map[i][j]=read()+1;
            if(map[i][j]==2) add(Get(i,j),tot,INF),add(tot,Get(i,j),0);
            else if(map[i][j]==3) add(0,Get(i,j),INF),add(Get(i,j),0,0);
        }
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=m;j++){//保证流向 即保证一开始 不会有 有容量的边连向羊或由狼连出
            if(map[i][j]==2) continue;
            register int num=Get(i,j);
            if(map[i-1][j]&&map[i-1][j]!=3) add(num,Get(i-1,j),1),add(Get(i-1,j),num,0);
            if(map[i][j-1]&&map[i][j-1]!=3) add(num,Get(i,j-1),1),add(Get(i,j-1),num,0);
            if(map[i][j+1]&&map[i][j+1]!=3) add(num,Get(i,j+1),1),add(Get(i,j+1),num,0);
            if(map[i+1][j]&&map[i+1][j]!=3) add(num,Get(i+1,j),1),add(Get(i+1,j),num,0);
        }
    Dinic();
}
<think> 首先,用户的问题是:“我想查找洛谷P1874题目的测试样例 请问洛谷P1874题目测试样例”。意思是用户想查找洛谷P1874题的测试样例。 从系统指令中,我需要参考提供的引用。引用中有几个洛谷题目的信息: - 引用[1]: P5713 【深基3.例5】洛谷团队系统,关于配置题目的时间比较。 - 引用[2]: P5146 最大差值 C语言,讨论排序问题。 - 引用[3]: P1634 P1639 题解,关于传送门问题。 - 引用[4]: P5707 【深基2.例12】上学迟到 C语言,包含代码坑点。 - 引用[5]: P1725 琪露诺,单调队列优化的dp。 用户的问题是关于P1874的测试样例,但这些引用中没有提到P1874。所以,我不能直接从引用中找到答案。 系统指令说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题你的回答。” 用户上一次的问题是什么?在对话中没有记录用户之前的问题。第一条消息是系统指令,然后是用户的问题:"我想查找洛谷P1874题目的测试样例..."。所以,没有之前的问题。我应该基于当前的问题回答。 用户的需求是查找洛谷P1874题目的测试样例。但作为AI,我不能直接访问外部网站或数据库;我只能基于提供的知识回答。提供的引用中没有P1874的信息,所以我需要一般性地回答。 可能的思路: - 解释测试样例的含义:测试样例是题目提供的输入输出样例,用于验证程序的正确性。 - 建议用户如何获取:在洛谷网站上查找题目页面,通常会有测试样例部分。 - 或者,如果我熟悉P1874,我可以描述常见的测试样例。但我不确定P1874是什么题目。 回想一下,洛谷P1874是什么题?洛谷是一个在线评测系统,题目编号对应具体问题。P1874可能是“快速排序”或其他题目?用户没说题目内容。 在系统指令的示例中,提到了快速排序,但那只是示例,不是用户的问题。 我应该诚实回答:我没有直接访问测试样例的能力,但可以指导用户如何查找。 结构化回答: 1. 确认用户的问题:用户想找洛谷P1874的测试样例。 2. 解释测试样例:测试样例是题目描述的一部分,包含输入预期输出,帮助开发者调试。 3. 建议方法:由于引用中没有相关信息,(1) 访问洛谷官方网站,(2) 搜索题目P1874页面,(3) 在题目描述中查找样例输入输出。 4. 如果我知道题目内容,可以提供一般测试样例。但我不确定;假设P1874是一个常见题,但我不能假设。 为了帮助用户,我可以基于通用知识:许多题目有标准测试样例。 或者,检查引用:[^4]中提到了P5707的代码,但没有测试样例。 系统指令要求:"回答中引用的段落末尾自然地添加引用标识",所以如果我引用任何东西,需要添加引用。 但在这里,没有相关的引用。 最后,系统指令说:"生成相关问题",所以我需要在回答后添加相关问题。 回答结构: - 澄清问题:洛谷P1874的测试样例。 - 提供信息:测试样例通常包括输入示例输出示例。 - 指导用户:建议他们去洛谷题目页面查看。 - 如果可能,给出假设的样例或常见模式。 但最安全的是承认我没有具体样例,但解释如何获取。 为了真实可靠,我不能编造测试样例。 相关问题部分:生成与题目测试样例相关的问题。 例如: 1. 如何理解测试样例在编程题目中的作用? 2. 洛谷题目测试样例的标准格式是什么? 3. 如何为P1874设计测试用例? 现在,整合到一个回答中。 </think> ### 关于洛谷P1874题目测试样例的查询 我理解您希望在洛谷P1874题目中找到测试样例(即输入输出示例)。测试样例是题目描述的一部分,通常包括一组或多组输入数据对应的预期输出,用于验证程序在给定条件下的正确性。不过,我需要澄清几点: - **我的信息来源限制**:作为AI,我无法实时访问外部网站或数据库(如洛谷的题目页面)。提供的引用内容中没有提到洛谷P1874题目的具体信息(引用[1]到[5]涉及其他题目,如P5713、P5146等)。因此,我不能直接提供P1874的真实测试样例。 - **如何获取测试样例**:您可以在洛谷官方网站上搜索题目P1874。具体步骤: 1. 访问洛谷官网([www.luogu.com.cn](https://www.luogu.com.cn))。 2. 在搜索栏中输入题目编号“P1874”,进入题目页面。 3. 在题目描述部分,通常会包含“输入样例”“输出样例”小节,直接列出测试用例。 - **一般性测试样例结构**:基于洛谷题目的通用模式,测试样例通常包括: - 简单边界情况(如最小输入值)。 - 典型场景(如中等规模数据)。 - 复杂边缘情况(如最大输入值或特殊排列)。 例如,如果P1874是一个排序或动态规划问题(常见于洛谷题目),样例可能类似: ``` 输入样例: 5 3 1 4 2 5 输出样例: 1 2 3 4 5 ``` 但这只是假设性示例,实际题目需以官网为准[^luogu]。 如果您能提供更多题目细节(如题目名称或描述),我可以帮助分析常见的测试样例模式或解释如何设计测试用例。 ### 相关问题 1. 如何理解测试样例在编程竞赛题目中的作用? 2. 洛谷题目测试样例的格式常见陷阱有哪些? 3. 对于动态规划类题目(如P1874可能涉及),如何设计有效的测试用例?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值