代码源Div2 305-407

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-1

 i 到 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位得到abcefabcabcef的子串)

一个串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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值