305 删删
给定一个字符串,你可以删除多个(可以是 0) 相同 的字符,这样操作之后,你能否得到一个回文串?如果能,求最小化删除的个数。
输入格式
多组数据。
每一组数据包含两行,分别为字符串的长度 N,以及一个仅由小写字母组成的字符串 S。
输出格式
对于每一组数据,输出一行。
如果不可能得到一个回文串,输出 −1。反之则输出最小操作次数。
样例输入
4
8
bilibili
3
qwq
9
daimayuan
7
xcpcxpc
样例输出
1
0
-1
2
解释:
在第一个例子中,删除开头的 b
得到 ilibili
。
第二个例子中,qwq
本身已回文,不需要操作。
第三个例子中,可以看到 daimayuan
不能靠仅删除一种字符得到一个回文串。
数据规模
- 1≤N≤10^5, 但保证 ∑N≤2×10^5
思路:
由于只有小写字母,加上数据范围小,可以直接枚举要删除的字母,代入判断是否满足条件,ans取能满足条件的最小值。如果枚举完所有字母后没有一个成立,输出-1。
代码:
#include<bits/stdc++.h>
using namespace std;
char a[200010];
int ans;
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
scanf("%s",a+1);
ans=1e9;
for(int i=0;i<26;++i)
{
char c='a'+i;
int l=1,r=n;
int cnt=0;
int f=0;
while(l<r)
{
if(a[l]!=a[r])
{
if(a[l]==c)
{
++cnt;++l;continue;
}
else if(a[r]==c)
{
++cnt;--r;continue;
}
else
{
f=1;break;
}
}
++l;--r;
}
if(f==0) ans=min(cnt,ans);
}
if(ans==1e9) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
306 快快变大
给定一个长度为 n 的数组 a1,a2,…,an,接下来进行 n−1 次操作。每次选择一个下标 x ,将 ax 和 ax+1 合并成 ax × ax+1 mod1000003 ,并且你会获得 (ax−ax+1)^2的分数。
所以每次操作后,数组的长度将会减 1,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
样例输入
3
1 2 3
样例输出
26
数据规模
所有数据保证 1≤n≤300,1≤ai≤10^6。
思路:区间dp
以 f [ i ][ j ]表示从i到j的最大合并分数,s[i][j]表示从i到j的乘积 ,这样 i , k 与 k+1 , j 合并时得数为 s [ i ][ j ],分数为(s [ i ][ k ] - s [ k+1 ][ j ])^2
递推公式:f[ i ][ j ]=max(f[ i ][ j ],f[ i ][ k ]+f[ k+1 ][ j ]+(s[ i ][ k ]-s[ k+1 ][ j ])^2) i+1~k~j-1i 到 j 的分数是:原分数与 i 到 k 分数加上 k+1 到 j 分数再加上两段区间合并的分数取最大。
需要初始化每段区间的合并分数。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
const int p=1000003;
ll sum[1010][1010];
ll f[1010][1010];
int a[1000];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
sum[i][i]=a[i];
}
for(int i=1;i<n;++i)
{
for(int j=i+1;j<=n;++j)
{
sum[i][j]=(a[j]*sum[i][j-1])%p;
}
}
for(int l=2;l<=n;++l)//长度
{
for(int i=1;i<=n-l+1;++i)//左
{
int j=i+l-1;//右
for(int k=i;k<j;++k)
{
f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+(sum[i][k]-sum[k+1][j])*(sum[i][k]-sum[k+1][j]));
}
}
}
printf("%lld",f[1][n]);
return 0;
}
307 饿饿 饭饭2
接着《饿饿 饭饭》 的故事,在两天后,食堂的工作人员回来了,整个食堂又回到了原来井井有条的状态。
两个月后,由于天气越来越热,大家的胃口越来越小了,作为食堂管理员的CC非常担心孩子们的身体健康,所以他决定开展一个活动来调动孩子们吃饭的积极性,顺便考验一下孩子们的数学水平。活动内容如下:
先让每一个孩子都抽一个球,每一个球上有一个数字, 然后给这个孩子n个数字,每一个孩子都有无数次操作机会,每一次都会选中一个数将它乘上2,或者乘上3,请问这个孩子可以通过上面的操作将这n个数都变成相同的吗?
如果回答正确,这个回答正确的孩子就可以得到一份免费的午餐,但是这对于孩子们来说是在是太困难了,但是他们都想吃到免费的午餐,所以他们都想请你告诉他们正确的答案,让他们都迟到免费的午餐。
输入格式
第1行给定一个数T,表示有T个小孩子请你告诉他正确的答案。
第2到T+1行,第1个数是每个孩子抽到的数字n,第2到n+1个数是对应的n个数字。
输出格式
如果可以变成相同的,输出YES
。如果不能变成相同的,输出NO
。
数据规模
1≤T≤100,1≤n≤2×10^5,1≤ai≤10^9
数据保证∑(T)(i=1)n≤2×10^5
样例输入
2
4 75 150 75 50
3 100 150 250
样例输出
YES
NO
思路:
将每个数看做他们的最大公约数乘上n个2和m个3和其他质数得到,如果存在这个其他质数,那么不论再乘几个2或3都不可能与其他数相等(这个质数若存在于每个数中,就算在最大公约数里了)。
先找出所有数的最大公约数,如果每个数除上最大公约数后能被2和3约掉,输出“YES”,否则输出“NO”。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
int a[200010];
inline int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,d=0;scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
d=gcd(a[i],d);
}
int f=0;
for(int i=1;i<=n;++i)
{
int k=a[i]/d;
//判断k中有没有除了3、2以外的因数
while(k%3==0) k/=3;
while(k%2==0) k/=2;
if(k!=1)
{
f=1;break;
}
}
if(f) printf("NO\n");
else printf("YES\n");
}
return 0;
}
401 子串分值和
对于一个字符串 S ,我们定义 f(S) 为 S 中出现的不同的字符个数。 例如 f(aba)=2,f(abc)=3,f(aaa)=1。
现在给定一个字符串 S (假设长度为 len),请你计算 ∑(i=0)(len−1)∑(j=i)(len−1)f(S[i:j])。
输入格式
输入一行包含一个由小写字母组成的字符串 S 。
输出格式
输出一个整数表示答案。
样例输入
ababc
样例输出
28
数据规模
所有数据保证字符串长度 len≤1000000,字符串下标从 0 到 len−1。
思路:
如果按照基本思路,数据过大,肯定超时。考虑更精简的方法。
对于一个字母,假设为a,从上一个a到自身,这段区间没有a,从自身到最后,都会有a,为f贡献了1。由此省去 j 层循环,优化了时间。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
ll ans;
int num[50];//字母上一次出现的位置
int main()
{
memset(num,-1,sizeof(num));
//首次坐标为0,但个数为1
string s;
cin>>s;
int n=s.length();
int sum=0;//实时不同字母数
for(int i=0;i<n;++i)
{
int k=s[i]-'a';
sum=i-num[k];//向前有i-num[k]个数 (带自身)
ans+=(sum)*(n-i);//向后有n-i个数 (带自身)
num[k]=i;
}
printf("%lld",ans);
return 0;
}
402 蒟蒻
便利蜂的货架上摆了一排蒟蒻果冻,搞得鶸尛鱻眼花缭乱......
对于每个果冻,都有一个价格 w 和口感 t。鶸尛鱻有一个购物篮子,在挑选蒟蒻果冻的时候,他有以下几种操作:
- 操作 1:把一个价格为 w,口感为 t 的果冻放入篮子。
- 操作 2:拿出篮子中 最为廉价 的果冻。
- 操作 3:拿出篮子中 口感最差 的果冻。(t 越小,口感越差)
鶸尛鱻不喜欢重复,当操作 1 的 价格或口感 与篮中已有果冻重复时,他会立刻将其放回货架。
经过 n 次操作后,鶸尛鱻确定了要购买的若干果冻,请你帮他求出篮子里果冻的总价格。
输入格式
第 11 行一个正整数 n,代表操作次数。
第 22 行至第 (n+1) 行,每行 一个或三个 整数,分别表示 op,w,t。
w 和 t 当且仅当 op=1 时存在。
输出格式
输出一个整数,表示篮子里果冻的总价格。
样例输入
6
1 1 1
1 2 5
2
1 3 3
3
1 5 2
样例输出
7
数据规模
所有数据保证 1≤n≤10^5,1≤w,t≤10^6,且保证输入合法。
思路:
此题要求快速插入非重数据,找到数据中的最小值删除,并有两个最小值权值,要将其联系起来。
可以用stl中的map来解决。
map<key,value>默认以key从小到大排序,count函数可以判断map中是否存在value值。若建立两个map,分别以w,t 为key;t,w 为value,可以解决最小值间的联系。完美符合题目要求,故用map解。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
map<int,int> p,t;
//map<key,value> 默认key从小到大排序
//p存价格,t存口感
//二者第二位互相存,方便快速查找
int main()
{
int n,ans=0;
scanf("%d",&n);
while(n--)
{
int op;
scanf("%d",&op);
if(op==1)
{
int a,b;
scanf("%d%d",&a,&b);
if(p.count(a)==0&&t.count(b)==0)//不存在
{
p[a]=b;
t[b]=a;
//互相存方便定位
}
}
else if(op==2)
{
t.erase(p.begin()->second);
//定位删除
p.erase(p.begin());
}
else if(op==3)
{
p.erase(t.begin()->second);
t.erase(t.begin());
}
}
ll sum=0;
for(auto i:p)//访问p中的元素
sum+=i.first;
printf("%lld\n",sum);
return 0;
}
403 锦标赛
题目描述
有n个玩家参加比赛,他们分别有能力值a1,a2,…,an。
需要进行n−1轮比赛,每一轮在剩下的玩家里任选两个玩家i,j。如果|ai−aj|>K,那么其中能力值高的玩家会获胜,能力值低的玩家会被淘汰。如果|ai−aj|≤K,那么两个玩家都有可能获胜,另一个玩家被淘汰。
n−1轮比赛之后,只剩下一个玩家。问有多少个玩家可能是最后获胜的玩家。
输入格式
第一行,两个整数n,K,表示玩家的总人数,和获胜条件中的参数。
接下来一行n个整数a1,a2,…,an,表示玩家的能力值。
输出格式
一个整数,表示最后可能获胜的玩家个数。
样例输入1
5 3
1 5 9 6 3
样例输出1
5
样例输入输出2
见下发文件。
数据规模
共10组数据。
测试点1满足n≤5。
测试点2满足n≤10。
测试点3,4,5满足n≤1000。
对于100%的数据,满足n≤10^5,1≤ai,K≤10^9。
思路:
将n个玩家从大到小排序,如果当前玩家能力值减去下一位玩家的能力值大于k,表明下一位玩家无论如何都不可能战胜当前玩家及之前的玩家,不可能获胜。
排序后相减,差值大于k时停止,统计玩家个数。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
int a[100010];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
int ans=1;
for(int i=1;i<n;++i)
{
if(a[i]-a[i+1]>k) break;
++ans;
}
printf("%d",ans);
return 0;
}
404 可重排列
请按字典序从小到大的顺序输出所有序列,满足序列中有 p1 个 1, p2个 2, ……, pn 个 n。
输入格式
第一行一个整数 n。
第二行 n 个整数 p1,p2,…,pn。
输出格式
按字典序从小到大的顺序一行一行输出所有满足条件的序列,每行一个序列,相邻两个数字需要用空格隔开。
样例输入
3
1 2 2
样例输出
1 2 2 3 3
1 2 3 2 3
1 2 3 3 2
1 3 2 2 3
1 3 2 3 2
1 3 3 2 2
2 1 2 3 3
2 1 3 2 3
2 1 3 3 2
2 2 1 3 3
2 2 3 1 3
2 2 3 3 1
2 3 1 2 3
2 3 1 3 2
2 3 2 1 3
2 3 2 3 1
2 3 3 1 2
2 3 3 2 1
3 1 2 2 3
3 1 2 3 2
3 1 3 2 2
3 2 1 2 3
3 2 1 3 2
3 2 2 1 3
3 2 2 3 1
3 2 3 1 2
3 2 3 2 1
3 3 1 2 2
3 3 2 1 2
3 3 2 2 1
数据规模
对于 100% 的数据,保证 1≤n≤9,1≤pi≤9,保证满足条件的序列个数不超过 105 个。
思路:
深搜dfs思想。统计每个数字使用次数,作为限制条件深搜,边界为达到指定长度。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
int num[100];
int len,n;
int ans[100];
int cnt[100];//这一位用了多少
void dfs(int dep)
{
if(dep>len)
{
for(int i=1;i<=len;++i)
{
printf("%d ",ans[i]);
}
printf("\n");
return;
}
for(int i=1;i<=n;++i)
{
if(cnt[i]<num[i])
{
ans[dep]=i;
++cnt[i];dfs(dep+1);
--cnt[i];
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i]);
len+=num[i];
}
dfs(1);
return 0;
}
405 进制转换
题面
让我看看是谁不会进制转换,哦原来是我
以不同进制的形式输入 n 个非负整数,求出它们的和并以 m 进制的形式输出。
使用大写字母 A
~ Z
依次代表 10 ~ 35, 小写字母 a
~ z
依次代表 36 ~ 61。
输入格式
第一行输入两个整数 1≤n≤10 , 2≤m≤62 。
接下来 n 行,每行输入一个整数 2≤t≤62, 一个 t 进制数 0≤x≤10^9。
输出格式
一个 m 进制数,为最终的结果
输入样例1
2 2
2 1
6 10
输出样例1
111
输入样例2
1 10
52 aA0
输出样例2
97864
输入样例3
2 52
36 AMD
52 YES
输出样例3
dJD
思路:
按照进制转换的思路,先转换为10进制求和,再转换为要求的进制。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
ll ans=0;
string ansm;
ll t1(int t,string s)
{
ll x=0;
ll k=1;
for(int i=s.length()-1;i>=0;--i)
{
int a;
if(s[i]>='0'&&s[i]<='9') a=s[i]-'0';
if(s[i]>='A'&&s[i]<='Z') a=s[i]-'A'+10;
if(s[i]>='a'&&s[i]<='z') a=s[i]-'a'+36;
x=x+a*k;k*=t;
}
return x;
}
void d1(ll ans,int m)
{
while(ans)
{
int k=ans%m;
ans/=m;
if(k>=0&&k<=9) ansm+='0'+k;
if(k<=35&&k>=10) ansm+='A'+(k-10);
if(k<=61&&k>=36) ansm+='a'+(k-36);
}
for(int i=ansm.length()-1;i>=0;--i)
printf("%c",ansm[i]);
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
int t;string s;
cin>>t>>s;
ans+=t1(t,s);
}
d1(ans,m);
return 0;
}
406 循环字串
题目描述
一个字符串S是另一字符串T的循环子串当且仅当存在k, T所有字符循环右移k位后得到的新串′ T ′,满足S是′ T ′的子串。
例如: abc
是 cefab
的循环子串。 (cefab
循环右移2位得到abcef
, abc
是abcef
的子串)
一个串P是完全循环串当且仅当对于它的任一子串H, 都有H reverse是P的循环子串 (H reverse 为 H的倒转, 如abc
reverse后 为cba
)。
给一个长度为n的字符串, 判断它是不是完全循环串。
输入格式
第一行一个正整数t, 表示测试数据组数。
对于每一组数据,第一行一个正整数n, 表示字符串的长度。接下来一行一个长度为n的字符串. 仅包含小写字母。
输出格式
对于每组测试数据,如果这个串是完全循环串, 输出YES
,否则输出NO
。每组测试数据之间输出换行。
数据范围
对于所有数据 有 1≤t≤100, 1≤n≤10^3, ∑n≤10^3。
样例输入
2
4
ccca
11
eeaafbddfaa
样例输出
YES
NO
提示 选中可以查看
1. 本道题目只需要语法知识就可以解决。
2. 任意子串是什么意思呢?
3. 如果一个子串包含另一个子串,那么我们是不是只需要求出大子串的合法情况,就可以推出小子串的合法情况。
4. 从大的子串向小的子串考虑 最大的子串是什么呢?
思路:
将字符串翻转,若反转后的串是翻转前的循环字串,那么所有翻转后H的子串也成立。操作是:先将字符串翻转(H'),将原字符串(H)翻倍(H2)并与翻转后的串比较,如果H'是H2的子串,则表明H'是H的翻转子串。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
char a[10000];
char b[10000];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
scanf("%s",a+1);
int f=0,tot=0;
for(int i=n+1;i<=2*n;++i) a[i]=a[i-n];
for(int i=1;i<=n;++i) b[n-i+1]=a[i];
for(int i=1;i<=n;++i)
{
int cnt=0;
for(int j=1;j<=n;++j)
{
if(a[i+j-1]==b[j]) ++cnt;
}
if(cnt==n)
{
f=1;break;
}
}
if(f==1) printf("YES\n");
else printf("NO\n");
}
return 0;
}
407 饿饿 饭饭之暑假大狂欢
故事接着《饿饿 饭饭 2》,又过了几个月,暑假来啦!!!
这天,cc和他的小伙伴们决定一起去游乐园玩,他们一天将游乐园的所有设施玩了个遍,甚至大摆锤,过山车他们还去了很多次,愉快的时间总是很短暂的,很快时间就来到了晚上,但是你以为一天的娱乐时光就这样结束了吗,那你就猜错啦。
晚上,游乐园晚上的party就开始啦,其中有一个游戏环节,赢的人可以得到免费的西瓜,饿到不行的cc和他的小伙伴非常希望得到这个西瓜。
包括cc和他的小伙伴,有t个玩家参与了这个游戏,每个玩家都有一张带有数字的卡片。第i张卡片上有ni个数字,分别是m1,m2,...mn。
游戏过程中,主持人从袋子里一个一个地取出编号的球。 他用洪亮而清晰的声音大声念出球的编号,然后把球收起来。 如果玩家的卡片上有对应的数字,就可以将它划掉。 最先从他的卡片上划掉所有数字的人获胜。 如果多人同时从他们的卡片上划掉所有数字,那么这些人都不能赢得比赛。 在游戏开始时,袋子里有 100 个球,编号从 1 到 100,所有球的编号都是不同的。
cc偷偷知道了每个玩家的数字。 想请你确定每个玩家是否可以在最有利于他的情况下赢得比赛。
输入格式
第一行给出一个数t,代表t个玩家。
接下来第二行到t+1行,每行第一个数为ni,代表这个人手中有n个卡片,接下来给出序列a1...an表示这个人所拥有的卡片的数字。
输出格式
输出t行,每一行给出第i个人在最有利的情况下是否能赢得比赛,可以输出YES
, 不可以输出NO
。
数据范围
1≤t≤100,1≤n≤100,1≤mi≤100
样例输入
3
1 1
3 2 4 1
2 10 11
样例输出
YES
NO
YES
思路:
如果一个人在对自己最有利的情况下,那主持人每次都能叫到自己手中的卡片,那么只有自己的号码完全包含其他人,才会先于或同时自己叫完那个被包含的人。
所以在轮到当前人时,判断是否完全包含其他人的号码,完全包含输出“NO”,不完全包含输出“YES”。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
int t[1100][1100];//i玩家的牌
int n;
int num[1100];
bool search(int n,int a[],int k)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)/2;
if(a[mid]<k) l=mid+1;
else r=mid;
}
if(a[l]==k) return 1;
else return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i]);
for(int j=1;j<=num[i];++j)
{
scanf("%d",&t[i][j]);
}
}
for(int i=1;i<=n;++i)
sort(t[i]+1,t[i]+num[i]+1);
for(int i=1;i<=n;++i)//在i中找j中元素
{
int j;
for(j=1;j<=n;++j)
{
if(i==j) continue;
int cnt=0;
for(int k=1;k<=num[j];++k)
{
if(search(num[i],t[i],t[j][k]))
{
++cnt;
}
}
if(cnt==num[j])
{
printf("NO\n");
break;
}
}
if(j==n+1) printf("YES\n");
}
return 0;
}