A 流放
描述:
yezzz 近日被流放到了北方的牧场养牛,er_sz 给了他一块面积为s平方米的矩形牧场,还需用栅栏围起来。已知每米栅栏 1 元,问最少花多少钱,便能将这块牧场围起来?
注意:牧场可为满足“长 × 宽 = 面积 s”条件的任意长宽的矩形,长宽皆为整数。
输入:
第一行,一个整数 T,表示有 T 组测试样例,题目保证 0<T≤100。
接下来 T 行,每行一个整数 s 表示牧场面积 (0≤s≤109)。
输出:
输出共 T 行,每行输出一个整数,表示对于该组样例 yezzz 最少花多少钱。
样例输入:
2 9 5
样例输出:
12 12
样例输入:
3 3 7 15
样例输出:
8 16 16
解题思路:
这道题数据量很大1e9*1e2的,肯定不能枚举,所以我们想更快的方法,1e9怎么简化。
我们知道面积是长×宽,所以我们枚举一边就可以了,这样可以直接降低一个平方根的复杂度
但是我们仔细一想,如果是奇数这种没法取平方怎么办呢,那也很简单,我们提前把答案设成minn = 2*(x+1),其中x代表的是长,1表示宽。
代码示例:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int t;
int s;
int main(){
cin>>t;
for(int k=1;k<=t;++k){
cin>>s;
int minn=(s+1)*2;
for(int i=2;i*i<=s;++i){
if(s%i==0){
minn=min(minn,2*(i+s/i));
}
}
cout<<minn<<endl;
}
}
B 抢糖果
描述:
万圣节那天,er_sz 从 yezzz 那里抢走了 n 堆糖果,然后为了表示自己还是讲武德的,他还制定了一个游戏规则:对于这 n 堆糖果,他只能一堆一堆的取(每次都要取完整的一堆),并且要保证到最后他获得的糖果总数是个合数。er_sz 很贪,他想知道他最多能取走多少堆糖果?
输入:
第一行,一个整数 T,表示有 T 组测试样例,题目保证 0<T≤100。
每组样例输入共 2 行。
第一行一个整数 n (0<n<104) 。
第二行有 n 个整数 a1,a2,...,an (表示第 i 堆糖果有 ai 个,0<ai≤104) 。
输出:
输出共 T 行,每行输出一个整数,表示对于该组样例 er_sz 最多能取多少堆糖果。
样例输入:
1 3 1 2 3
样例输出:
3
样例输入:
2 6 2 2 2 2 2 3 5 1 2 3 4 7
样例输出:
5 4
注释:
数据保证一定有解!
解题思路:
合数是素数对应的数集,记住这一点你就成功一半了,我们加和所有的a数组,结果如果是合数就直接输出n,如果是素数,除了2都是奇数必然是有多出来的奇数,减去这个就可以了,也就是n-1
代码示例:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int t;
int n;
int x;
int cnt;
bool isprime(int a){
if(a==2) return 1;
for(int i=2;i<=sqrt(a);i++){
if(a%i==0) return 0;
}
return 1;
}
int main(){
cin>>t;
while(t--){
cin>>n;
cnt=0;
for(int i=1;i<=n;i++){
cin>>x;
cnt+=x;
}
if(isprime(cnt)) cout<<n-1<<endl;
else cout<<n<<endl;
}
}
D 魔鬼的步伐
描述:
yezzz 今天心情"很好",因为 er_sz 要请他干饭!已知出发点在 (a,b),饭馆在 (c,d),yezzz 每一步可以向东南西北任意方向移动(每一步移动1个单位),但 er_sz 有个要求:连续的两步不能沿着同一方向移动。
给定 (a,b) 和 (c,d),请计算 yezzz 最少需要几步移动到饭馆。
求最少步数。可以证明,他永远可以到达终点。
输入:
第一行,一个整数 T,表示有 T 组测试样例,题目保证 0<T≤100。
接下来 TT 行,每行四个整数 a,b,c,d 代表一组数据,其中 (a,b) 为起点,(c,d) 为终点。(−104<a,b,c,d<104)
输出:
输出 T 行,第 i 行代表第 i 组数据的答案。
样例输入:
3 -2 0 -2 1 0 1 3 3 -1 1 1 1
样例输出:
1 5 4
解题思路:
你在光滑的地上摩擦就可以了
他的限制条件是不能连续向同一方向走两次,所以我们要绕个远路,也就是本来 →→ 就可以了,但是不能连续的话就只能→↑→↓才行。我们对这道题要先求出从一点到另一点会有几次向左(右)和几次向上(下),然后我们把差值算出来,如果大于1就要考虑绕远路了。并且绕远路回来又可以同原来方向继续走一次然后再绕远路。
代码示例:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t;
int a,b,c,d;
int x,y;
int main(){
cin>>t;
while(t--){
cin>>a>>b>>c>>d;
x=abs(a-c);
y=abs(b-d);
int cnt=abs(x-y);
int ans=x+y;
if(cnt<=1) cout<<ans<<endl;
else{
if(cnt%2==0) ans+=cnt;
else ans+=cnt-1;
cout<<ans<<endl;
}
}
return 0;
}
F Σ逛超商
描述:
在观看了著名教育片《杰哥不要》后,Σ 先生受到了极大的启发,并立志决定从今日(当然,也可能是明日)起开始认真学算法,并成为像杰哥一样的人!
《杰哥不要》里逛超商的一幕深深吸引了 Σ,于是她决定效仿其中的内容,并带上一个最多可容纳 k 件物品的手推车开始逛超商!值得一提的是,在进入超商之前,她的手推车中可能已经存放了若干件物品。
当然了,Σ 即将临幸的超商非常与众不同!这个超商一共有 n 个货架,在第 i 个货架面前,Σ 将购买或卖出 ai 件物品,当 ai>0 时,表示 Σ 在此处购买了 ai 件物品,ai<0 则表示 ΣΣ 在此处卖出了 ai 件物品。
在任何一个货架面前,手推车中的物品总数都不能超过 k ,当然也不可能小于 0 。现在 Σ 希望你能求出,进入超商前手推车内的物品数量共有多少可能性?
注:每个货架上的商品与手推车内的物品性质相同,Σ 可在任意货架将这些物品买入或卖出。
输入:
第一行包含两个整数 n , k。其中 n 代表货架的个数,k 代表手推车最大可容纳的商品数。保证 1≤n≤1000,1≤k≤。
第二行包含 n 个整数 a1,a2,…,an 。第 i 个整数代表 Σ 在第 ii 个货架上交易的物品数量。保证 −≤ai≤。
输出:
一个整数,表示 Σ 进入超商前,手推车内商品共有多少种合法情况。
样例输入:
3 5 2 1 -3
样例输出:
3 对于样例 1 ,初始时 Σ 手推车中的数目可以有 0,1,2 个,共三种情况。
样例输入:
2 4 -1 1
样例输出:
4
样例输入:
4 10 2 4 1 2
样例输出:
2
分析题目:
现有一个数字 m,将对其进行若干次增加或减少的操作,并要求每次操作后该数字不允许大于 k (题目已给定)或小于 0 ,问数字 m 可能是几种不同的数字(如果无可能则输出 0 )
解题思路:
本题的关键是找到在这些操作中,该数字 m 最多会增加及减少的数目,而由于每一步操作后的结果都与之前的所有步骤有关,因此可以考虑运用前缀和累加操作(显然单纯累加也可以)
代码示例:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int n,k;
int x;
int cnt;
int minn;
int maxx;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>x;
cnt+=x;
maxx=max(maxx,cnt);
maxx=min(maxx,k);
minn=min(cnt,minn);
}
if(minn>0){
if((k-maxx+1)<=0) cout<<"0"<<endl;
else cout<<k-maxx+1<<endl;
}
else{
if((k-maxx+minn+1)<=0) cout<<"0"<<endl;
else cout<<k-maxx+minn+1<<endl;
}
return 0;
}
G Σ与酒神
描述:
或许你并不知道,HeartFireY 其实是 ACS 的主(酒)人(神)!且由于某些特殊原因,Σ 先生的地位极低,只能担当给 HeartFireY 倒酒的工作。
在某个被 HeartFireY 承包的酒吧里共有 n 瓶不同的酒和 m 个相同的酒杯,其中第 i 瓶酒的容量是 ti 升(保证 ti 为正整数)。值得一提的是,HeartFireY 在喝酒时有一个奇怪的癖好,即:他不希望一瓶酒被倒进两个以上的杯子里,但他能接受将不同的酒混在一个杯子里!
为了让 HeartFireY 被服务(?)的十分满意,Σ 先生抽空练成了一项绝不手抖的神技,这使得她每一秒能够稳定从每一个酒瓶里倒出一升的酒(不要问,问就是魔法)。倒酒师 Σ 可以选择花费 ti 的时间将第 i 瓶酒直接倒入某个指定的杯子里,也可以选择将第 i 瓶酒分为 ai 升和 bi 升并花费对应的时间分别倒入两个不同的杯子。注意:拆分后的 ai 和 bi 必须是正整数,且满足 ai + bi = ti。
然而,由于所有的酒瓶都只有一个瓶口,因此在同一时段内,每瓶酒只能倒入一个杯子。且一个杯子也不允许在同一时段内被两瓶不同的酒同时倒入,但允许起止时间重叠,例如:若前一瓶酒在第 3 秒结束倒入,则后一瓶酒必须在至少第 3 秒起开始倒入。
Σ 希望能够尽快摆脱作为一个倒酒师的命运,因此她希望倒酒的过程能够结束的越快越好!现在请你帮助她想出一个倒酒的方法,使得 Σ 能够早日翻身做主人。需要特别注意的是,Σ 懒得和狗一样,若当前有多个杯子可供选择,且选择任意一个杯子均不改变倒酒的总时间,则她将选择编号其中最小的杯子。
输入:
第一行包含两个整数 n 和 m,分别代表酒的瓶数和酒杯的数量,保证 1≤n,m≤.
第二行包含 n 个整数 t1,t2,…,tn。第 i 个整数代表第 i 瓶酒的容量。 0<ti≤
输出:
共输出 i 行,其中第 i 行代表第 i 瓶酒的倒酒计划。
每一行的第一位数字为整数 k ( k=1 或 2 ),表示 Σ 将把这瓶酒倒进 k 个酒杯里。随后将以时间顺序输出 k 个数对 (id,l,r) ,其中 id 代表倒入的酒杯编号,l 为倒入该酒杯的开始时间,r 为倒入该酒杯的结束时间。保证 1≤id≤m,0≤l<r≤
样例输入:
5 3 1 2 3 4 5
样例输出:
1 1 0 1 1 1 1 3 2 2 0 1 1 3 5 1 2 1 5 1 3 0 5
注释:
以下是对样例一的补充说明: 本题中,可行且同样最优的方案还有:
1 1 0 1
1 2 0 2
1 2 2 5
1 1 1 5
1 3 0 5
但由于 Σ 很懒,她将在可行且最优的情况下每次选择编号最小的酒杯,因此该样例的标准答案如输出样例中所示。其中输出样例第三行的意思为:对于第三瓶酒,Σ 将把它倒进两个酒杯里,在第 0−1 秒,这瓶酒将倒进编号为 2 的酒杯,在第 3−5 秒,这瓶酒将倒进编号为 1 的酒杯。由于按时间顺序排列,输出时倒入编号 2 酒杯在倒入编号 1 酒杯的前面。
遵循此条件后,本题答案唯一。
解题思路:
将 n 瓶不同的酒倒入 m 个相同的酒杯里,每瓶酒最多允许倒入两个杯子,且保证能够每秒从每瓶酒内倒出 1 L 的酒,问在最小时间内倒完所有酒的方案。本题中,不难得知需要花费的最小时间为【单瓶酒的最大容量,所有酒容量/杯子数(该结果注意上取整)】中的较大值。以该较大值为每个杯子的容量上限,逐一遍历即可。若当前杯子的剩余容量可直接倒完某瓶酒,则仅需当前一个杯子,否则倒满该杯子并将剩余部分倒进下一杯。注意本题数据范围会爆 int
代码示例:
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long ll;
ll a[N],sum,ma;
int n,m;
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
ma=max(ma,a[i]);
}
ll ave=sum/m;
if(sum%m!=0)//处理向上取整
ave++;
sum=max(ave,ma);//sum为最优状态下每个杯子最多倒入的酒
ll t=sum,id=1;//此时每个酒杯剩余容量都为sum 并从编号为1的开始
for(int i=0;i<n;i++){
if(t>=a[i]){//说明该酒杯的剩余容量足够一次性倒完
cout<<1<<" "<<id<<" "<<sum-t<<" "<<sum-t+a[i]<<endl;
//因为前面可能已经倒过了,所以起始应该是sum-t
t-=a[i];//此酒杯剩余容量减少a[i]
if(t==0){//如果倒完这瓶酒后 杯子的剩余容量为0
id++;//更换到下一个杯子
t=sum;//下一个杯子起始时剩余的容量为sum
}
}
else{
/*使用两个杯子,那么就先把前一个还有剩余容量但是不够一次性倒完的杯子使用了,可以在该杯子中倒入t升
剩余还需将a[i]-t升倒入下一个杯子里,开始:0,所需:a[i]-t
*/
cout<<2<<" "<<id+1<<" "<<0<<" "<<a[i]-t<<" "<<id<<" "<<sum-t<<" "<<sum<<endl;
id++;//由于第id号杯子已经被倒满了 因此换到下一个
t=sum-(a[i]-t);//下一个杯子的剩余容量为sum-(a[i]-t);
}
}
return 0;
}
J er_sz被压迫了
描述:
虽然 yezzz 搬过几次砖,但因为某些不知名原因,他再次返贫了,所以为了脱贫,他决定拿出自己练习多年的本领——织围巾,想依靠这个本领摆脱贫困。为了织出更好看的围巾,他请 er_sz 帮忙在他织的围巾上设计一些装饰。在 yezzz 的万(暴)般(力)请(压)求(迫)下,er_sz 满含眼泪的在所有围巾上写分别了一个仅由 26 个小写字母构成的字符串。不过这个字符串并没有得到 yezzz 的满意,于是 yezzz 在告(暴)别(打)er_sz 之后,决定自己动手解决。yezzz 想将所有的字符串变成他心目中完美的字符串——回文串。yezzz 想让你告诉他,他是否可以选定字符串中出现过的任意一个字母,通过在字符串删除这个字母(删除个数不确定,可能不删除,可能全部删除;删除位置也不确定)得到一个回文串,与此同时,因为yezzz 比较懒,所以他想通过删除最少的字母得到一个回文串。请告诉 yezzz,他是否可以通过这种操作得到一个回文串,如果可以,输出最少需要删除多少字母;如果不可以,输出 −1。
注意:删除的字母必须和选定的字母相同。
回文串:对于一个字符串,如果从左到右看和从右到左看完全一致,我们称这个字符串为回文串。比如:字符串 ′kek′ , ′abacaba′ , ′r′和 ′papicipap′都是回串,而,′abcabc′ 则不是回文串。
输入:
第一行是一个整数 T (1≤T≤100) — 测试样例的数量。接下来的 2∗T行是对每一个测试样例的描述。
对于每个测试样例的描述包含两行,第一行是一个整数 n(1≤n≤105) 表示字符串的长度,第二行是绣在围巾上的字符串。
输出:
对于每个测试用例,如果可能的话,输出使字符串成为回文所需的最小操作次数,如果不可能的话,输出 −1 。
样例输入:
5 8 abcaacab 6 xyzxyz 4 abba 8 rprarlap 10 khyyhhyhky
样例输出:
2 -1 0 3 2
注释:
在第一个测试用例中,你可以选择一个字母 ′a′ 并擦除它的第一次和最后一次出现,你会得到一个字符串 ′bcaacb′′bcaacb′,这是一个回文。你也可以选择一个字母 ′b′,擦掉它的所有出现,你会得到一个字符串 ′acaaca′,这也是一个回文。 在第二个测试用例中,可以看出,不可能选择一个字母并擦除它的一些出现来获得回文。 在第三个测试用例中,您不需要删除任何符号,因为字符串已经是一个回文。
解题思路:
对于一个字符拆你,我们是否可以通过删除某个字母(可以将该字母全部删除,也可以都不删除),得到一个回文串,如果可以最少需要删除多少;不可以输出-1;
回文串的定义就是,从前往后遍历和从后往前遍历,得到结果是相同的。
那么对于回文串的遍历,我们就可以使用 “双指针算法” 来进行遍历,即分别用两个变量标记我们从头和从尾开始遍历到了哪里(这两个变量实际上就是数组的两个下标)。假设我们选定删除字母 'a' 以期望得到一个回文串,那么在我们遍历过程中,对于这两个 “指针” 所指向的字母,我们进行一次判断,如果这两个字母相同,那么我们继续遍历(因为我们要求最小删除次数,所以,即便这俩个字母都为 ‘a' 我们也不进行删除操作);如果某一个 “指针” 指向的字母为 'a' 那么我们将该位置的 'a' 删除,然后继续遍历,如果两个"指针"指向的字母不同,那么我们停止遍历。
因为本题的字符串都是由小写字母构成(也就是说,字符串中只有26种字符),所以,想要知道删除哪一个字母符合题目要求,我们可以进行一次循环,从 'a' 一直到 'z' 最后得出最小删除数目,这个循环中就可以进行上述过程,知道完成26次遍历得到答案(min 或者 -1)。
代码示例:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n, m, t;
char str[N];
void solve()
{
scanf("%d%s", &n, str + 1);
int ans = -1;
for (char i = 'a'; i <= 'z'; i++) // 从 'a' 到 'z' 进行遍历
{
int cnt = 0, l = 1, r = n;
while (l < r)
{
if (str[l] == str[r])//两个字母相同,继续遍历
{
l++;
r--;
}
else
{
if (str[r] == i && str[l] != i)
{
cnt++;
r--;
}
else if (str[l] == i && str[r] != i)//有一个字母等于 i 删除后继续遍历
{
cnt++;
l++;
}
else if (str[l] != str[r]) //字母不相同,遍历结束
{
cnt = 1e9;
break;
}
}
}
if (cnt != 1e9 && (ans == -1 || cnt < ans))
ans = cnt;
}
printf("%d\n", ans);
}
int main()
{
scanf("%d", &t);
while (t--)
{
solve();
}
}
L er_sz又双叒叕压迫了
描述:
他来了,他又来了,yezzz 又来压迫 er_sz 了,这次他给了 er_sz 一个相对简单的任务:有 T 个 整数,对于每一个整数 n ,判断是否可以通过删除其中的几位(可能没有进行删除操作)使得这个数可以被 25 整除,如果可以,输出最少需要删除几位数
如:251,删除最后一位 1 得到 25 输出 1
215,删除第二位 1 得到 25 输出 1
250,不需要删除即符合要求 输出 0
输入:
第一行一个整数 T (1≤T≤1000) 接下来 TT 行,每行一个整数 n (25≤n≤108)
输出:
每行一个整数,表示需要删除的位数(不需要删除,输出 0 )
样例输入:
5 100 71345 3259 50555 2050047
样例输出:
0 3 1 3 2
解题思路:
给一个数字,想要通过删除其中的某几位(可能不需要删除)得到一个新的数字,使这个数可以被 25 整除,输出最小删除次数。
题目保证对于给出的每一个样例,都可以通过题目所述操作使其可以被25整除。
对于一个数,如果可以被 25 整除,那么这个数最后两位一定是 25/50/75/00 有且仅有这四种情况。所以,我们只需要知道如果得到这四种情况中的一种,最少需要删除多少数字即可。
代码示例:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int main()
{
int t;
cin >> t;
while (t--)
{
int ans = 0;
cin >> n;
if (n % 25 == 0)
cout << ans << endl;
else
{
int fg1 = 0;
int fg2 = 0;
int c;
while (n)
{
ans++;
c = n % 10;
if(fg1 && (c == 2 || c == 7))
{
cout<<ans - 2 <<endl;
break;
}
if(fg2 && (c == 0 || c == 5))
{
cout<<ans - 2 <<endl;
break;
}
if(c == 0)
fg2 = 1;
if(c == 5)
fg1 = 1;
n /= 10;
}
}
}
}