洛谷_P2586 [ZJOI2008]杀蚂蚁(模拟)

[ZJOI2008]杀蚂蚁

题目链接:https://www.luogu.com.cn/problem/P2586

题意

模拟题,题意太长,直接读题吧。

题解:

大模拟。按照题目要求写就好,中间套了点计算几何。设结构体维护每个蚂蚁的信息,因为总数较少,所以删除某个点的时候直接把后面的点前移就好了。对于每个事件,写个函数就好了。注意严格按照题目说的做。
注意一些坑点,蚂蚁不动也可以拿到蛋糕。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6
 
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 30;
const int mod = 998244353;
struct art{
	int t, hp, hpmax, exp, x, y, stu, upx, upy;
	art(){stu = 0;upx = upy = -1;}
	art(int a, int b, int c, int d, int e){
		t=a, exp = c, hpmax = hp = b, x = d, y = e;
		stu = 0;
		upx = upy = -1;
	}
}mayi[10];
//sig:蛋糕是否还在,num:活着的蚂蚁数量,ov:游戏是否结束
int n, m, num, sig, ov;
//a:信息素数量,c:位置是否是可行点
int x[maxn], y[maxn], a[10][10], c[10][10];
int dx[4]={0,1,0,-1}, dy[4]={1,0,-1,0};
bool internal(int x, int y);
void getdir(art &b, int t);
void UpdInfo();
void ArtDie(int i);
void Attack(int x, int y, int d, int r);
int dist(int x1, int y1, int x2, int y2);
double PointToLine(int x, int y, int a1, int b1, int a2, int b2);

int main()
{
	int t, s, d, r, i, j, k;
	int sum = 0;
	double xue = 1.1;
	num = 0, sig = 1, ov = 0;
	scanf("%d %d %d %d %d", &n, &m, &s, &d, &r);
	memset(c, 0, sizeof(c));
	for(i=0;i<s;i++)
	{
		scanf("%d %d", &x[i], &y[i]);
		c[x[i]][y[i]] = 1;
	}
	scanf("%d", &t);
	memset(a, 0, sizeof(a));
	
	for(i=1;i<=t;i++)
	{
		if(num < 6 && !c[0][0]){
			int mx = 4*xue+eps;
			mayi[num] = art(i, mx, sum/6+1, 0, 0);
			c[0][0] = 1;
			sum++, num++;
			if(sum%6 == 0)xue*=1.1;
		}
		//蚂蚁移动
		for(j=0;j<num;j++)
		{
			getdir(mayi[j], i-mayi[j].t+1);
			//判断能否拿蛋糕
			if(mayi[j].x == n && mayi[j].y == m && sig)
			{
				mayi[j].stu = 1;
				sig = 0;
				mayi[j].hp = min(mayi[j].hp+mayi[j].hpmax/2, mayi[j].hpmax);
			}
		}
		//塔台攻击
		for(j=0;j<s;j++)
			Attack(x[j], y[j], d, r);
		//清除死亡蚂蚁
		for(j=0;j<num;j++)
			if(mayi[j].hp <0){
				ArtDie(j);
				j--;
			}
		//判断蛋糕是否被运回窝
		for(j=0;j<num;j++)
			if(mayi[j].x == 0 && mayi[j].y == 0 && mayi[j].stu==1)
				ov = 1;
		if(ov)
		{
			printf("Game over after %d seconds\n", i);
			printf("%d\n", num);
			for(j=0;j<num;j++)
				printf("%d %d %d %d %d\n", i-mayi[j].t,
						mayi[j].exp, mayi[j].hp, mayi[j].x, mayi[j].y);
			break;
		}
		//更新信息素
		UpdInfo();
	}
	if(!ov)
	{
		printf("The game is going on\n");
		printf("%d\n", num);
		for(j=0;j<num;j++)
			printf("%d %d %d %d %d\n", t-mayi[j].t+1,
					mayi[j].exp, mayi[j].hp, mayi[j].x, mayi[j].y);
	}
	
	return 0;
}
//蚂蚁移动
void getdir(art &b, int t)
{
	int i, j = -1, nx, ny, mx = -1;
	//信息素更新
	if(b.stu == 1) a[b.x][b.y]+=5;
	else a[b.x][b.y] += 2;

	for(i=0;i<4;i++)
	{
		nx = b.x+dx[i], ny = b.y+dy[i];
		if(nx == b.upx && ny == b.upy)continue;
		if(internal(nx, ny) && a[nx][ny] > mx && c[nx][ny]==0)
		{
			mx = a[nx][ny];
			j = i;
		}
	}
	
	//规则4
	if(t % 5 == 0)
		for(i=1;i<=5;i++)
		{
			nx = b.x+dx[(j-i+4)%4], ny=b.y+dy[(j-i+4)%4];
			if(nx == b.upx && ny == b.upy)continue;
			if(internal(nx, ny) && c[nx][ny] == 0){
				j = (j-i+4)%4;break;
			}
		}
	if(j == -1){
		b.upx = b.x, b.upy = b.y;
		return ;
	}
	nx = b.x + dx[j], ny = b.y+dy[j];
	c[b.x][b.y] = 0, c[nx][ny] = 1;
	b.upx = b.x, b.upy = b.y;
	b.x = nx, b.y = ny;
}

bool internal(int x, int y)
{
	if(x>=0 && x<=n &&y>=0 && y<=m)return true;
	else return false;
}

//更新信息素
void UpdInfo()
{
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			if(a[i][j]>0)a[i][j]--;
}
//i号蚂蚁死亡
void ArtDie(int i)
{
	c[mayi[i].x][mayi[i].y] = 0;
	if(mayi[i].stu == 1)sig = 1;
	for(;i+1<num;i++)
		mayi[i] = mayi[i+1];
	num--;
}
//炮台攻击蚂蚁
void Attack(int x, int y, int d, int r)
{
	int i, j, k;
	LL mx = -1;
	//查找是否可以攻击背上有蛋糕的
	for(i=0;i<num;i++)
	{
		if(mayi[i].stu == 1 && dist(x,y,mayi[i].x, mayi[i].y) <= (LL)r*r)
			break;
	}
	if(i < num){
		mayi[i].hp -= d;
		j = i;
		for(i=0;i<num;i++)
		{
			if(i!=j && PointToLine(mayi[i].x, mayi[i].y,x,y,mayi[j].x,mayi[j].y)-0.5<=eps)
				mayi[i].hp -= d;
		}
		return;
	}
	//攻击能攻击到的最近的蚂蚁
	j = -1;
	for(i=0;i<num;i++)
	{
		LL dis = dist(x,y,mayi[i].x, mayi[i].y);
		if( dis <= (LL)r*r && (mx==-1 || dis < mx))
			mx = dis, j = i;
	}
	if(j == -1 || mx == -1LL)return;

	mayi[j].hp -= d;
}

int dist(int x1, int y1, int x2, int y2)
{
	return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
//判断点到直线距离。
double PointToLine(int x, int y, int a1, int b1, int a2, int b2)
{
	double mu = (a2-a1*1.0)*(a2-a1)+(b2-b1*1.0)*(b2-b1);
	double zi = (x-a1*1.0)*(a2-a1)+(y-b1*1.0)*(b2-b1);
	double t = zi / mu;
	double mx, my, result;
	if(t>=0 && t<=1)
	{
		mx = a1+(a2-a1)*t;
		my = b1+(b2-b1)*t;
		result = (x-mx)*(x-mx)+(y-my)*(y-my);
	}
	else
	{
		result = min((x-a1*1.0)*(x-a1*1.0)+(y-b1*1.0)*(y-b1), 
					(x-a2)*(x*1.0-a2)+(y-b2)*(y*1.0-b2));
	}
	return sqrt(result);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值