99. 激光炸弹
一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。
若目标位于爆破正方形的边上,该目标将不会被摧毁。
输入描述
输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi 。
输出描述
输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。
示例1
输入
2 1
0 0 1
1 1 1
输出
1
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5500;
int s[N][N],res;
int n,r,mx,my;
int main()
{
scanf("%d%d",&n,&r);
r=min(r,5001);
mx=5100,my=5100;
for(int i=0;i<n;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
x++,y++;
s[x][y]+=w;
mx=max(mx,x);
my=max(my,y);
}
for(int i=1;i<=mx;i++)
{
for(int j=1;j<=my;j++)
{
s[i][j]+=+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
}
if(r>mx&&r>my) res = s[mx][my];
else
{
for(int i=r;i<=mx;i++)
for(int j=r;j<=my;j++)
{
res=max(res,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);
}
}
cout<<res;
}
1230. K倍区间
资源限制
时间限制:1.0s 内存限制:256.0MB
给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
输入格式
-----
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
-----
输出一个整数,代表K倍区间的数目。
例如,
输入格式
5 2
1
2
3
4
5
程序应该输出:
6
本题直接想法是暴力枚举,固定左右端点,对区间和的数相加判断是否是k的倍数,算法复杂度太高;
对于内层循环,可以用前缀和减少运算量;
对于右侧每个r,我们有在1到r上,找出(s[r]-s[l-1])是k的倍数的左端点即可,0到r-1上;s[r]与s[l]的余数相同的端点满足要求,也就是求与每一个s[r]%k相同的在r之前的数的个数,可以通过一个数组记录s[r]%k的个数。最后通过循环算出,需要注意的是,s[r]%k余数为0的书本身就可以作为k的倍数的区间,因此需要算入s[r]的个数。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],s[N],res[N];
int n,k;
int main(){
cin>>n>>k;
long long ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=(s[i-1]+a[i])%k;
ans+=res[s[i]];
res[s[i]]++;
}
cout<<ans+res[0];
}
买不到的数目
问题描述
小明开了一家糖果店。
他别出心裁:把水果糖包成4颗一包和7颗一包的两种。
糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。
当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。
大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
输入格式
两个正整数 n,m,表示每种包装中糖的颗数。
输出格式
一个正整数,表示最大不能买到的糖数。
数据范围
2≤n,m≤1000,
保证数据一定有解。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
int m,n;
cin>>m>>n;
cout<<m*n-m-n;
}
蚂蚁感冒
长 100厘米的细长直杆子上有 n 只蚂蚁。
它们的头有的朝左,有的朝右。
每只蚂蚁都只能沿着杆子向前爬,速度是 1厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。
这些蚂蚁中,有 1只蚂蚁感冒了。
并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。
输入格式
第一行输入一个整数 n, 表示蚂蚁的总数。
接着的一行是 n 个用空格分开的整数 Xi, Xi 的绝对值表示蚂蚁离开杆子左边端点的距离。
正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。
其中,第一个数据代表的蚂蚁感冒了。
输出格式
输出1个整数,表示最后感冒蚂蚁的数目。
数据范围
1<n<50
0<|Xi|<100
输入样例1:
3
5 -2 8
输出样例1:
1
输入样例2:
5
-10 8 -20 12 25
输出样例2:
3
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int x[51];
int main(){
int n,ans=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>x[i];
}
int left=0,right=0;
for(int i=0;i<n;i++){
if(abs(x[0])<abs(x[i])&&x[i]<0)right++;
if(abs(x[0])>abs(x[i])&&x[i]>0)left++;
}
if(x[0]<0&&!left||x[0]>0&&!right){
ans=1;
}
else{
ans+=left+right+1;
}
cout<<ans;
}
蓝桥杯–饮料换购
乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去,但不允许赊账。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的n瓶饮料,最后他一共能得到多少瓶饮料。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
int tot=0,s=0,ans;
cin>>tot;
ans=tot;
while(tot/3>0){
ans+=tot/3;// 可换数
tot=tot/3+tot%3; //可换数+剩余瓶盖数目;
}
cout<<ans;
}
摘花生
Hello Kitty想摘点花生送给她喜欢的米老鼠。
她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。
Hello Kitty只能向东或向南走,不能向西或向北走。
问Hello Kitty最多能够摘到多少颗花生。
输入格式
第一行是一个整数T,代表一共有多少组数据。
接下来是T组数据。
每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。
每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。
输出格式
对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。
数据范围
1≤T≤100,1≤R,C≤100,0≤M≤1000
输入样例:
2
2 2
1 1
3 4
2 3
2 3 4
1 6 5
输出样例:
8
16
#include<iostream>//递推性
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=101;
int t;
int s[N][N];
int main(){
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>s[i][j];
if(i==0&&j==0){
s[i][j]=s[i][j];}
else if(i==0){
s[i][j]=s[i][j-1]+s[i][j];
}
else if(j==0){
s[i][j]=s[i-1][j]+s[i][j];
}
else {s[i][j]=max(s[i-1][j],s[i][j-1])+s[i][j];}
}
}
cout<<s[n-1][m-1]<<endl;
}
}
#include<iostream> //递归型
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=101;
int t;
int d[N][N],s[N][N];
int dfs(int x,int y){
if(x<1||y<1){
return 0;
}
if(s[x][y]!=-1){
return s[x][y];
}
else{ if(x==1&&y==1){
return d[x][y];
}
s[x][y]=max(dfs(x-1,y),dfs(x,y-1))+d[x][y];
return s[x][y];}
}
int main(){
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>d[i][j];
s[i][j]=-1;
}
}
cout<<dfs(n,m)<<endl;
}
}
01背包问题
问题描述
01背包问题可描述为如下问题:
有一个容量为V的背包,还有n个物体。现在忽略物体实际几何形状,我们认为只要背包的剩余容量大于等于物体体积,那就可以装进背包里。每个物体都有两个属性,即体积w和价值v。
问:如何向背包装物体才能使背包中物体的总价值最大?
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int v[MAXN]; // 体积
int w[MAXN]; // 价值
int f[MAXN][MAXN]; // f[i][j], 总体积不超过j体积前i个物品的所有方案最大价值
int main()
{ int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
if(j<v[i]){
f[i][j]=f[i-1][j];
}
else{
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
}
cout<<f[n][m];
}
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int v[MAXN]; // 体积
int w[MAXN]; // 价值
int f[MAXN]; //
int main()
{ int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m];
}
最长上升子序列
给出一个由 n(n≤5000) 个不超过 1e6 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n,表示序列长度。
第二行有 n 个整数,表示这个序列。
输出格式
一个整数表示答案。
输入输出样例
输入
6 1 2 4 1 3 4
输出
4
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[5001],s[5001];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
s[i]=1;
for(int j=0;j<i;j++){
if(a[j]<a[i]){
s[i]=max(s[i],s[j]+1);
}
}
}int ans=0;
for(int i=0;i<n;i++){
ans=max(s[i],ans);
}
cout<<ans<<endl;
}
1 地宫寻宝
X 国王有一个地宫宝库,是 n×m 个格子的矩阵,每个格子放一件宝贝,每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。
输入格式
第一行 3 个整数,n,m,k,含义见题目描述。
接下来 n 行,每行有 m 个整数 Ci 用来描述宝库矩阵每个格子的宝贝价值。
输出格式
输出一个整数,表示正好取 k 个宝贝的行动方案数。
该数字可能很大,输出它对 1000000007 取模的结果。
数据范围
1≤n,m≤50,
1≤k≤12,
0≤Ci≤12
输入样例1:
2 2 2
1 2
2 1
输出样例1:
2
输入样例2:
2 3 2
1 2 3
2 1 5
输出样例2:
14
#include<iostream> //递归
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=51,mod=1000000007;
int f[N][N][13][14];//从开始到 i,j位置所取得的宝物数为k,最后一件取得价值为c的宝物的所有集合的方案
int a[N][N];
int n,m,k;
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
a[i][j]++;
}
}
f[1][1][1][a[1][1]]=1;
f[1][1][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1&&j==1)
continue;
for(int u=0;u<=k;u++){
for(int v=0;v<=13;v++){
int &val=f[i][j][u][v];
val=(val+f[i-1][j][u][v])%mod;
val=(val+f[i][j-1][u][v])%mod;
if(u>0&&v==a[i][j]){
for(int w=0;w<v;w++){
val=(val+f[i-1][j][u-1][w])%mod;
val=(val+f[i][j-1][u-1][w])%mod;
}
}
}
}
}
}
int ans=0;
for(int i=0;i<=13;i++){
ans=(ans+f[n][m][k][i])%mod;
}
cout<<ans<<endl;
}
波动数列
问题描述
观察如下数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加 2 或者减少 3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a或者减少 b 的整数数列可能有多少种呢?
输入格式
输入的第一行包含四个整数 n s a b ,含义如前面说述。
输出格式
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以 100000007 的余数。
样例输入
4 10 2 3
样例输出
2
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=100000007;
int f[1001][1001]; //前i个数,所有总和除n的余数是j的所有集合
int get_mod(int a,int b){
return (a%b+b)%b;
}
int main(){
int n,s,a,b;
cin>>n>>s>>a>>b;
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<n;j++){
f[i][j]=(f[i-1][get_mod(j+b*i,n)]+f[i-1][get_mod(j-a*i,n)])%mod;
}
}
cout<<f[n-1][get_mod(s,n)]<<endl;
}
连号区间数
小明这些天一直在思考这样一个奇怪而有趣的问题:
在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式:
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
输出格式:
输出一个整数,表示不同连号区间的数目。
示例:
用户输入:
4
3 2 4 1
程序应输出:
7
用户输入:
5
3 4 2 5 1
程序应输出:
9
解释:
第一个用例中,有7个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [2,2], [3,3], [4,4]
第二个用例中,有9个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [1,5], [2,2], [3,3], [4,4], [5,5]
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e4;
int a[N];
int main(){
int n,ans=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
int max1=-1;
int min1=1000000;
for(int j=i;j<n;j++){
max1=max(max1,a[j]);
min1=min(min1,a[j]);
if(max1-min1==j-i){
ans++;
}
}
}
cout<<ans;
}