目录
1.激光炸弹
题目描述
地图上有 N 个目标,用整数 Xi,Yi 表示目标在地图上的位置,每个目标都有一个价值 Wi。
注意:不同目标可能在同一位置。
现在有一种新型的激光炸弹,可以摧毁一个包含 R×R个位置的正方形内的所有目标。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,y 轴平行。
求一颗炸弹最多能炸掉地图上总价值为多少的目标。
输入格式
第一行输入正整数 N和 R,分别代表地图上的目标数目和正方形包含的横纵位置数量,数据用空格隔开。
接下来 N行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi,分别代表目标的 x坐标,y坐标和价值,数据用空格隔开。
输出格式
输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。
数据范围
0≤R≤1e9
0<N≤10000,
0≤Xi,Yi≤5000
0≤Wi≤1000
输入样例
2 1
0 0 1
1 1 1
输出样例
1
思路
每次枚举的都是一个长度为R的小正方形方格内的和,求出所有的这样的小方格中的最大值,
利用二维前缀和数组,构造好前缀和,每次枚举大小为R的正方形方格。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5100;
int s[N][N];
int n,R;
int main()
{
cin>>n>>R;
R=min(R,5001);
int ans=0;
while (n -- )
{
int x,y,w;
cin>>x>>y>>w;
x++,y++;
s[x][y]+=w;
}
for(int i=1;i<=5001;i++)
for(int j=1;j<=5001;j++)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
for(int i=R;i<=5001;i++)
for(int j=R;j<=5001;j++)
{
int p=s[i][j]-s[i-R][j]-s[i][j-R]+s[i-R][j-R];
ans=max(ans,p);
}
cout<<ans;
return 0;
}
2.我在哪?
农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!
沿路有一排共 N个农场。
不幸的是农场并没有编号,这使得约翰难以分辨他在这条路上所处的位置。
然而,每个农场都沿路设有一个彩色的邮箱,所以约翰希望能够通过查看最近的几个邮箱的颜色来唯一确定他所在的位置。
每个邮箱的颜色用 A..Z 之间的一个字母来指定,所以沿着道路的 N个邮箱的序列可以用一个长为 N的由字母 A..Z组成的字符串来表示。
某些邮箱可能会有相同的颜色。
约翰想要知道最小的 K的值,使得他查看任意连续 K个邮箱序列,他都可以唯一确定这一序列在道路上的位置。
例如,假设沿路的邮箱序列为 ABCDABC
。
约翰不能令 K=3,因为如果他看到了 ABC
,则沿路有两个这一连续颜色序列可能所在的位置。
最小可行的 K的值为 K=4,因为如果他查看任意连续 4 个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。
输入格式
输入的第一行包含 N,第二行包含一个由 N 个字符组成的字符串,每个字符均在 A..Z 之内。
输出格式
输出一行,包含一个整数,为可以解决农夫约翰的问题的最小 K值。
数据范围
1≤N≤100
输入样例
7
ABCDABC
输出样例
4
做题思路
题目中的数据是100,可以依次从小到大枚举所有可能长度的字符串,然后判断
该长度的字符串子串是否符合题目中只出现一次的要求,开一个map容器对其进
行判断。
//得到字符串x中下标为[l,r]之间的子串。
string get(string x,int l,int r)
{
string res;
for(int i=l;i<=r;i++) res+=x[i];
return res;
}
//依次枚举符合条件的长度的最小值
for(int len=0;len<=x.size();len++)
{
int flag=1;//判断当前长度是否符合
map<string,int> h;
for(int i=0;i<x.size()-len;i++)
{
if(h[get(x,i,i+len)]==1)//已经出现了一次,不符合条件
{
flag=0;
break;
}
h[get(x,i,i+len)]++;
}
if(flag)
{
cout<<len+1;
break;
}
}
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
string get(string x,int l,int r)
{
string res;
for(int i=l;i<=r;i++) res+=x[i];
return res;
}
int main()
{
int n;
cin>>n;
string x;
cin>>x;
for(int len=0;len<=x.size();len++)
{
int flag=1;
map<string,int> h;
for(int i=0;i<x.size()-len;i++)
{
if(h[get(x,i,i+len)]==1)
{
flag=0;
break;
}
h[get(x,i,i+len)]++;
}
if(flag)
{
cout<<len+1;
break;
}
}
return 0;
}
3.回文日期
20202020 年春节期间,有一个特殊的日期引起了大家的注意:20202020 年 22 月 22 日。
因为如果将这个日期按 “yyyymmdd
” 的格式写成一个 88 位数是 20200202
,恰好是一个回文数。
我们称这样的日期是回文日期。
有人表示 20200202
是“千年一遇” 的特殊日子。
对此小明很不认同,因为不到 22 年之后就是下一个回文日期:20211202
即 2021 年 12月 22 日。
也有人表示 20200202
并不仅仅是一个回文日期,还是一个 ABABBABA
型的回文日期。
对此小明也不认同,因为大约 100100 年后就能遇到下一个 ABABBABA
型的回文日期:21211212
即 21212121 年 12月 12 日。
算不上“千年一遇”,顶多算“千年两遇”。
给定一个 8位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA
型的回文日期各是哪一天。
注意
下一个回文日期和下一个 ABABBABA
型的回文日期可能是同一天。
ABABBABA
型的回文日期,需要满足 A≠B。
输入格式
输入包含一个八位整数 N,表示日期。
输出格式
输出两行,每行 1 个八位数。
第一行表示下一个回文日期,第二行表示下一个 ABABBABA
型的回文日期。
输入样例
20200202
输出样例
20211202
21211212
(1)判断日期是否合法
分离年月日,特殊判断是否为闰年,分别判断是否合法。
(2)判断回文
从输入的日期进行枚举,依次找到第一个回文日期和第一个ABABBABA型。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define N 5
using namespace std;
int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int check_vaild(int n)
{
int year = n / 10000 , month = n % 10000 / 100 , day = n % 100;
if(month < 0 || month > 12) return false;
if(day < 0 || (day > days[month] && month != 2)) return false;
int x = 0;
if(month == 2)
x = year % 4 == 0 && year % 100 || year % 400 == 0;
if(day > days[month] + x) return false;
return true;
}
int check_ABAB(int n)
{
int a = n / 10000000 , b = n / 1000000 % 10 , c = n /100000 % 10 , d = n / 10000 % 10;
if(a == c && b == d && a != b) return true;
return false;
}
int main()
{
int n;
scanf("%d",&n);
int falg = 1;
for(int i = n / 10000;i < 100000;i ++)
{
int x = i , t = i;
for(int j = 0;j < 4;j ++)
x = x*10 + t%10 , t /= 10;
if(check_vaild(x) && x > n && falg)
{
printf("%d\n",x);
falg = 0;
}
if(check_vaild(x) && check_ABAB(x) && x > n)
{
printf("%d\n",x);
break;
}
}
return 0;
}
4.分巧克力
儿童节那天有 K位小朋友到小明家做客。
小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 N块巧克力,其中第 i块是 Hi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 N块巧克力中切出 K块巧克力分给小朋友们。
切出的巧克力需要满足:
- 形状是正方形,边长是整数
- 大小相同
例如一块 6×5的巧克力可以切出 6 块 2×2的巧克力或者 2 块 3×3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入格式
第一行包含两个整数 N和 K。
以下 N行每行包含两个整数 Hi和 Wi。
输入保证每位小朋友至少能获得一块 1×1的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
数据范围
1≤N,K≤105,
1≤Hi,Wi≤105
输入样例
2 10
6 5
5 6
输出样例
2
思路
如果切出的巧克力的边长为mid,并且满足最大的边长,满足二分的条件,
求出满足条件的最大的mid,也就是求出满足条件的最后一个出现的mid,
求出最后一个满足条件的二分模板:
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
二分的条件为,切出来的巧克力的数目>=k:
bool check(int mid)
{
int ans=0;
for(int i=1;i<=n;i++)
ans+=(H[i]/mid)*(W[i]/mid);
if(ans>=k) return true;
else return false;
}
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10;
int H[N],W[N];
int n,k;
bool check(int mid)
{
int ans=0;
for(int i=1;i<=n;i++)
ans+=(H[i]/mid)*(W[i]/mid);
if(ans>=k) return true;
else return false;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d%d", &H[i],&W[i]);
int l=1,r=1e5;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d",l);
return 0;
}