打卡信奥刷题(3511)用C++实现信奥题 P10865 [HBCPC2024] Genshin Impact Startup Forbidden III

P10865 [HBCPC2024] Genshin Impact Startup Forbidden III

题目描述

Blue-edged Shot 被 LeavingZ 禁止玩《原神》。然而,今天 LeavingZ 前往了华中科技大学的网络科学与工程学院,参加2024年中国湖北省国际大学生程序设计竞赛,并收获了金牌。

《原神》中的一个活动嘟嘟可砰砰历险记已经开始了。这是一个单人游戏,每局游戏都涉及一个池塘。池塘可以被划分为一个 n×mn×mn×m 的网格,其中第 iii 行第 jjj 列的单元格表示为 (i,j)(i,j)(i,j)。在这些单元格中,有 kkk 个单元格包含鱼,你将扮演火花骑士可莉,她用炸药来捕鱼。

如果可莉在 (a,b)(a,b)(a,b) 位置投放炸弹,那么所有满足∣x−a∣+∣y−b∣≤1|x-a|+|y-b|\le 1xa+yb1的单元格 (x,y)(x,y)(x,y) 都将被爆炸覆盖。对于每一个被爆炸覆盖的单元格,可莉都会从其中捕到一条鱼。可莉可以在任何位置投放炸弹。问题是,为了捕到所有的鱼,至少需要多少枚“蹦蹦炸弹”?

输入格式

第一行包含三个整数 n,m,kn,m,kn,m,k (1≤n,m≤103,1≤k≤101 \le n,m \le 10^3, 1 \le k \le 101n,m103,1k10),分别表示网格的大小(行数和列数)以及包含鱼的单元格数量。

接下来的 kkk 行,每行包含三个整数 xi,yi,aix_i,y_i,a_ixi,yi,ai (1≤xi≤n,1≤yi≤m,1≤ai≤31\le x_i \le n, 1 \le y_i \le m, 1 \le a_i \le 31xin,1yim,1ai3),表示在网格的 (xi,yix_i, y_ixi,yi) 单元格中有 aia_iai 条鱼。

输入保证所有的 (xi,yi)(x_i, y_i)(xi,yi) 单元格坐标都是唯一的。

输出格式

输出一个整数,表示所需的最少炸弹数量。在这个问题中,你需要计算并输出一个整数,该整数代表为了捕获所有鱼所需投放的最少“蹦蹦炸弹”数量。

输入输出样例 #1

输入 #1

5 5 3
1 1 2
2 2 1
5 5 2

输出 #1

4

说明/提示

一种可能的方法是在 (1,2)(1,2)(1,2) 位置投放两枚炸弹,再在 (5,5)(5,5)(5,5) 位置投放另外两枚炸弹。

可以证明没有比这个答案更小的解。

C++实现

#include <bits/stdc++.h>
#define init(x) memset (x,INF,sizeof (x))
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 2e6 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int n,m,k,p,tot,v[15],dp[MAX],sx[] = {0,1,-1,0,0},sy[] = {0,0,0,1,-1};
vector <int> tmp;
set <pair <int,int> > loc;
map <pair <int,int>,int> id;
int main (){
	n = read ();m = read ();k = read ();
	for (int i = 0;i < k;++i)	{
		int x = read (),y = read ();v[i] = read ();
		id[{x,y}] = i;//用 map 记录下对应的位置
		for (int j = 0;j < 5;++j)		{
			int xx = x + sx[j],yy = y + sy[j];
			if (1 <= xx && xx <= n && 1 <= yy && yy <= m) loc.insert ({xx,yy});//预处理出炸弹可能放置的位置,用 set 去重
		}
		tot = tot * 4 + v[i];//处理出对应的四进制数
	}
	init (dp);dp[0] = 0;
	for (int i = 0;i <= (1 << (2 * k));++i)	{
		if (dp[i] == INF) continue;
		for (auto nxt : loc)		{
			int x = nxt.first,y = nxt.second;
			p = i;tmp.clear ();
			for (int j = 1;j <= k;++j) tmp.push_back (p % 4),p /= 4;//逐位分离
			for (int j = 0;j < 5;++j)			{
				int xx = x + sx[j],yy = y + sy[j];
				if (id.find ({xx,yy}) == id.end ()) continue;//不需要放置炸弹
				int o = id[{xx,yy}];
				tmp[k - o - 1] = min (v[o],tmp[k - o - 1] + 1);//炸弹最多不超过格子中鱼的数量  注意下标
			}
			p = 0;
			for (int i = tmp.size () - 1;~i;--i) p = p * 4 + tmp[i];//重新计算出新的状态
			dp[p] = min (dp[p],dp[i] + 1); 
		}
	}
	printf ("%d\n",dp[tot]);
	return 0;
}
inline int read (){
    int s = 0;int f = 1;
    char ch = getchar ();
    while ((ch < '0' || ch > '9') && ch != EOF)	{
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9')	{
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * f;
}

在这里插入图片描述

后续

接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值