time:2.5
一、
题目描述
给定一个 N × M 的矩阵 A,请你统计有多少个子矩阵 (最小 1 × 1,最大 N × M) 满足子矩阵中所有数的和不超过给定的整数 K?
输入格式
第一行包含三个整数 N, M 和 K.
之后 N 行每行包含 M 个整数,代表矩阵 A.
输出格式
一个整数代表答案。
样例输入
3 4 10 1 2 3 4 5 6 7 8 9 10 11 12
样例输出
19
1、简单的暴力算法:
直接循环每个元素并包含该元素进行行循环,列循环若超过目标值,则终止。
2.改进算法:
将列元素相加,并存储在二维数组中
#include <stdio.h>
#include <iostream>
using namespace std;
int s[505][505];
int main()
{
int a = 0, b = 0, c = 0;
cin >> a >> b >> c;
for (int i = 1; i <= a; i++)
{
for (int j = 1; j < b; j++)
{
cin >> s[i][j];
s[i][j] += s[i - 1][j];
}
}
long long int res = 0;
for (int i = 1; i < a; i++)
{
for (int j = i; j <= a; j++)
{
for (int left = 1, right = 1, sum = 0; right <= b; right++)
{
sum += s[j][right] - s[i - 1][left];
while (sum>c)
{
sum -= s[j][left] - s[i - 1][left];
left++;
}
res += right - left + 1;
}
}
}
printf("%lld", res);
return 0;
}
二、
题目 2657:
修剪灌木
时间限制: 3s 内存限制: 320MB 提交: 8139 解决: 4285
题目描述
爱丽丝要完成一项修剪灌木的工作。有 N 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晚会修剪一棵灌木,让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始,每天向右修剪一棵灌木。当修剪了最右侧的灌木后,她会调转方向,下一天开始向左修剪灌木。直到修剪了最左的灌木后再次调转方向。然后如此循环往复。灌木每天从早上到傍晚会长高 1 厘米,而其余时间不会长高。在第一天的早晨,所有灌木的高度都是 0 厘米。爱丽丝想知道每棵灌木最高长到多高。
输入格式
一个正整数 N ,含义如题面所述。
输出格式
输出 N 行,每行一个整数,第i行表示从左到右第 i 棵树最高能长到多高。
样例输入
3
样例输出
4 2 4
提示
对于 30% 的数据,N ≤ 10. 对于 100% 的数据,1 < N ≤ 10000.
#include<stdio.h>
#include<iostream>
using namespace std;
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int num;
cin >> num;
for (int i = 1; i <= num; i++){
printf("%d\n", max(i - 1, num - i) * 2);
}
return 0;
}
三、
题目 2656:
刷题统计
时间限制: 3s 内存限制: 320MB 提交: 29063 解决: 4749
题目描述
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n 题?
输入格式
输入一行包含三个整数 a, b 和 n.
输出格式
输出一个整数代表天数。
样例输入
10 20 99
样例输出
8
提示
对于 50% 的评测用例,1 ≤ a, b, n ≤ 106 . 对于 100% 的评测用例,1 ≤ a, b, n ≤ 1018 .
1.会因为运行时间拿不到满分
#include<stdio.h>
#include<iostream>
using namespace std;
int a = 0, b = 0, n = 0;
int j[100000000];
int d = 0;
int main()
{
while (1)
{
int sum = 0;
cin >> a >> b >> n;
if (a < b)
{
d = n / a;
}
else if (a >= b)
{
d = n / b;
}
for (int i = 0; i < (d + 3); i++)
{
if (((i + 1) % 7) <= 5 && ((i + 1) % 7) != 0)
{
j[i] = a;
}
else if (((i + 1) % 7) == 0 || ((i + 1) % 7) == 6)
{
j[i] = b;
}
}
int num = 0;
while (sum < n)
{
sum += j[num];
num++;
}
printf("%d", num);
}
return 0;
}
2.
#include<stdio.h>
int main()
{
long long int a,b,n,sum=0;
scanf("%lld%lld%lld",&a,&b,&n);
long long int flag=n/(a*5+b*2);//几个完整的周数
n-=(a*5+b*2)*flag;
sum+=flag*7;
if(n/a<=5)
{
sum+=n/a;
if(n/a==5)
{
sum+=(n-5*a)/b+(n-5*a-b)%1;
}
else if(n%a>0)
{
sum++;
}
}else{
sum+=5+(n-5*a)/b;
if((n-5*a)%b>0)
{
sum++;
}
}
printf("%lld",sum);
return 0;
}
四、
小明最近迷上了一款名为《扫雷》的游戏。其中有一个关卡的任务如下, 在一个二维平面上放置着 n 个炸雷,第 i 个炸雷 (xi , yi ,ri) 表示在坐标 (xi , yi) 处存在一个炸雷,它的爆炸范围是以半径为 ri 的一个圆。
为了顺利通过这片土地,需要玩家进行排雷。玩家可以发射 m 个排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 j 个排雷火箭 (xj , yj ,rj) 表示这个排雷火箭将会在 (xj , yj) 处爆炸,它的爆炸范围是以半径为 rj 的一个圆,在其爆炸范围内的炸雷会被引爆。同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。现在小明想知道他这次共引爆了几颗炸雷?
你可以把炸雷和排雷火箭都视为平面上的一个点。一个点处可以存在多个炸雷和排雷火箭。当炸雷位于爆炸范围的边界上时也会被引爆。
输入格式
输入的第一行包含两个整数 n、m.
接下来的 n 行,每行三个整数 xi , yi ,ri,表示一个炸雷的信息。
再接下来的 m 行,每行三个整数 xj , yj ,rj,表示一个排雷火箭的信息。
输出格式
输出一个整数表示答案。
样例输入
2 1 2 2 4 4 4 2 0 0 5
样例输出
2
#include <stdio.h>
struct node
{
int x, y, d;
} a[100000], p[100010];
int vis[100000], t, n;
long long juli(int x, int y, int xx, int yy)
{
return (long long)((x - xx)*(x - xx) + (y - yy)*(y - yy));
}
int main()
{
int i, j, m;
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].d);
for (i = 1; i <= m; i++)
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].d);
int l = 0, r = m + 1;
while (++l<r)
{
for (i = 1; i <= n; i++)
{
//用vis来记录是否已经入队
if (vis[i] == 0 && juli(p[l].x, p[l].y, a[i].x, a[i].y) <= (long long)p[l].d*p[l].d)//要是在范围内就加入队列变成排雷火箭
{
vis[i] = 1;
t++;
p[r++] = a[i];
}
}
}
printf("%d", t);
return 0;
}
改进(用二分法查找来防止内存超限)
#include<stdio.h>
struct node
{
int x, y, d;
} a[100000], p[100010];
int vis[100000], t, n;
int kuai(int xx, int yy)//给炸雷的x排序
{
int i = xx, j = yy;
struct node t = a[(i + j) / 2];//取中
do
{
while (a[i].x>t.x) j--;
if (i <= j)
{
struct node m = a[i];
a[i] = a[j];
a[j] = m;
i++;
j--;
}
} while (i <= j);
if (xx<j) kuai(xx, j);
if (i<yy) kuai(i, yy);
}
long long juli(int x, int y, int xx, int yy)
{
return (long long)((x - xx)*(x - xx) + (y - yy)*(y - yy));
}
int fun(int sum)//二分法查找在横坐标为你sum位置的点排在炸弹的第几位
{
int r = n, l = 1, mid;
while (l<r)
{
mid = (r + l) / 2;
if (sum <= a[mid].x) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int i, j, m;
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].d);
for (i = 1; i <= m; i++)
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].d);
kuai(1, n);
int l = 0, r = m + 1;//炮弹数量
while (++l<r)
{
int xx, yy;
xx = p[l].x - p[l].d, yy = p[l].x + p[l].d;//表示左右的范围
xx = fun(xx - 1), yy = fun(yy + 1);//可能会出现相等的数据,而只包含到其中的一个,所以扩展所查找的范围即可
for (i = xx; i <= yy; i++)//搜寻在该范围内的炸雷//缩小了搜索范围
{
if (vis[i] == 0 && juli(p[l].x, p[l].y, a[i].x, a[i].y) <= (long long)p[l].d*p[l].d)//要是在范围内就加入队列变成排雷火箭
{
vis[i] = 1;
t++;
p[r++] = a[i];
}
}
}
printf("%d", t);
return 0;
}
五、
描述
小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。
河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降 1,当石头的高度下降到 0 时小青蛙不能再跳到这块石头上(某次跳跃后使石头高度下降到 0 是允许的)。
小青蛙一共需要去学校上 x 天课,所以它需要往返 2x 次。当小青蛙具有一个跳跃能力 y 时,它能跳不超过 y 的距离。
请问小青蛙的跳跃能力至少是多少才能用这些石头上完 x 次课。
输入格式
输入的第一行包含两个整数 n, x,分别表示河的宽度和小青蛙需要去学校的天数。请注意 2x 才是实际过河的次数。
第二行包含 n − 1 个非负整数 H1, H2, · · · , Hn-1,其中 Hi > 0 表示在河中与小青蛙的家相距 i 的地方有一块高度为 Hi 的石头,Hi = 0 表示这个位置没有石头。
输出格式
输出一行,包含一个整数,表示小青蛙需要的最低跳跃能力。
样例输入
5 1 1 0 1 0
样例输出
4
提示
由于只有两块高度为 1 的石头,所以往返只能各用一块。第 1 块石头和对岸的距离为 4,如果小青蛙的跳跃能力为 3 则无法满足要求。所以小青蛙最少需要 4 的跳跃能力。
对于 30% 的评测用例,n ≤ 100;
对于 60% 的评测用例,n ≤ 1000;
对于所有评测用例,1 ≤ n ≤ 105 , 1 ≤ x ≤ 109 , 1 ≤ Hi ≤ 104。
#include<stdio.h>
#include<iostream>
using namespace std;
const int mmax = 1e5 + 100;
int hei[mmax]; //用来存放特定位置石头的高度
int front[mmax]; //用来存放前缀和
typedef long long ll;
ll ans; //最后答案
ll n, x;
bool isok(ll mid)
{
//最关键的是--每一个长度为 i 的区间的石头高度和都大于等于2x吗
for (ll i = 1; i <= n - mid; ++i)
{
ll r = i + mid - 1;
if (front[r] - front[i - 1] < 2 * x)
return false;
}
return true;
}
int main()
{
while (1)
{
cin >> n >> x;//河的宽度与上学的天数
for (int i = 1; i <= n - 1; ++i)
{
cin >> hei[i];
front[i] = front[i - 1] + hei[i];
}
int le = 1;
int ri = n;
int mid = le + (ri - le) / 2;
while (le < ri)
{
if (isok(mid))//在前半段范围
{
ans = mid;
ri = mid - 1;
mid = le + (ri - le) / 2;
}
else{//在后半段范围
le = mid + 1;
mid = le + (ri - le) / 2;
}
}
cout << ans << endl;
}
return 0;
}