安全逃离的题解

也农夫john最近在研究如果发生重大事故,如何让农场里的奶牛逃离问题。他想要确信在紧急情况下,所有的奶牛都有一个安全逃离方案。因为在紧急情况下,奶牛们都会失去观察和判断能力,所以最近john一直在教奶牛们逃离的方法,他的方法很简单,就是任何时候都只向北方或东方逃离,北方是行坐标减1的方向,东方是列坐标加1的方向。奶牛们虽笨,不过这一点事关自己的生命,所以他们牢记在心,而且也一定会这么做。
当然也会出问题,奶牛们在逃离的方向上会横冲直撞,为了阻止奶牛之间互相冲撞造成伤害,john要求任何一个奶牛的逃离路线不能经过其它奶牛的初始位置。一个逃离方案是安全的如果它能够满足上面的要求,反之它就是不安全的。
奶牛们所在的土地(农场)被划分成了r行和c列的一个矩形地图。奶牛们都待在这个矩形中的某一个位置。
请帮助john确定给定的一个地图上是否存在一个安全的逃离方案。
比如,下面的两个图:
左边的例子表示了一个能够安全逃离的地图,因为没有任何一个奶牛的逃离路线上包括其他奶牛。右边的例子表示了一个不安全的地图,因为位于(4,1)的奶牛不论是向东逃离还是向北逃离,它的路线上都会有别的奶牛,从这个图中拿掉任意一头奶牛,这个地图都会变成安全的。

      安全               不安全
  | | | | C--         C . . . . . 
  | | | | C--         ^ . . . . . 
  | C | | C--         | . . . . . 
  C C-+-+----         C------>C . 
  . . C C C--         . . . . . . 

C表示奶牛,直线表示逃离路线

错点

蒟蒻向老师报告题目有问题后没有认真看题,结果就死了。

分析

做到这道题的时候,我们考到数据范围很小,考虑高复杂度的做法。

我的做法是最暴力的做法,复杂度大概是 O ( c r 2 × + r c 2 × n ) O(cr^2 \times + rc^2 \times n) O(cr2×+rc2×n),面对这么小的这么小的数据,这个做法显然能过。

我们先写一个 c h e c k check check 函数,判断此时的矩阵是否安全。

bool check(){
    for(int i=1;i<=r;++)
        for(int j=1;j<=c;j++)
            if(a[i][j]){//此点有牛
                int s=0;
                for(int l=1;l<=i;l++)
                    if(a[l][j]){
                        s++;
                        break;
                    }
                for(int l=j;l<=n;l++)
                    if(a[i][l]){
                        s++;
                        break;
                    }
                if(s==2)return false;//如果有牛上面和右面都被堵死了,这个矩阵显然是不安全的
            }
    return true;
}

当然还有更快速地写法啦

bool save(){
	for(int i=1;i<=k;i++){
		int flag=0;
		for(int l=x[i]-1;l>=1;l--)
			if(a[l][y[i]]){
				flag++;
				break;
			}
		for(int l=y[i]+1;l<=n;l++)
			if(a[x[i]][l]){
				flag++;
				break;
			}
		if(flag==2)return false;
	}
	return true;
}

我们再写一个函数去枚举移掉哪头牛

void work(){
	int f=1;
	for(int i=1;i<=k;i++){
		a[x[i]][y[i]]--;
		if(save()){//如果安全
			f=0;
			write(i);//输出
			puts("");
		}
		a[x[i]][y[i]]++;
	}if(f)puts("-1");//无解
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
const int MAXN=50+10,MAXK=100+10;
int a[MAXN][MAXN],n,m,k,x[MAXK],y[MAXK];
bool save(){
	for(int i=1;i<=k;i++){
		int flag=0;
		for(int l=x[i]-1;l>=1;l--)
			if(a[l][y[i]]){
				flag++;
				break;
			}
		for(int l=y[i]+1;l<=n;l++)
			if(a[x[i]][l]){
				flag++;
				break;
			}
		if(flag==2)return false;
	}
	return true;
}
void work(){
	int f=1;
	for(int i=1;i<=k;i++){
		a[x[i]][y[i]]--;//移除
		if(save()){
			f=0;
			write(i);
			puts("");
		}
		a[x[i]][y[i]]++;//注意回溯
	}if(f)puts("-1");
}
int main(){
	read(n);read(m);read(k);
	for(int i=1;i<=k;i++){
		read(x[i]);read(y[i]);
		a[x[i]][y[i]]++;
	}
	if(save())puts("0");
	else work();
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值