SDWU 2021 Autumn Training Series C1 2st Round

A. Football
【题目大意】给你n个字符串,统计出现次数最多的字符串
【思路】map计数,找到出现次数最多的字符串
【参考代码】

#include <bits/stdc++.h>
using namespace std;
map<string, int> ma;
int n, ji;
string s, lu;
int main() {
    cin >> n;

    for (int i = 1; i <= n; i ++) {
        cin >> s;
        ma[s] ++;

        if (ma[s] > ji) {
            ji = ma[s];
            lu = s;
        }
    }

    cout << lu;
}

B. cAPS lOCK
【题目大意】给定一个字符串,若该字符串全为大写或仅第一位是小写,其余位全部为大写则把每个字母的大小写翻转,否则输出原字符
【思路】简单模拟题,显然第一个字母是大小写对该字符串是不是特定要求的字符串没有影响,首先判断是否是需要改动的字符串,若是则改变不是则直接输出原字符串
【参考代码】

#include <bits/stdc++.h>
using namespace std;
string s;
bool check(string x) {
    int len = x.size();

    for (int i = 1; i < len; i ++)
        if (x[i] > 'Z')
            return false;

    return true;
}
string change(string x) {
    int len = x.size();

    for (int i = 0; i < len; i ++)
        if (x[i] > 'Z')
            x[i] -= 32;
        else
            x[i] += 32;

    return x;
}
int main() {
    cin >> s;

    if (check(s))
        cout << change(s);
    else
        cout << s;
}

C. pSort
【题目大意】给定n的一个排列,每个数都有一个对应的di,每个数可以和与其距离为di的数进行任意次交换,求时候能弄1,2,3,4……n转化到当前序列
【思路】(并查集)由于交换是可逆的,我们可以把问题转化成将当前序列转化为1到n,由于每个数可以与与其相距di的数进行无数次交换,也就是说可以把当前数与与其相距di的数看成一个集合,集合内的数可以任意交换,我们把数字i出现的位置记做used[i],如果对于任意i位置上的数,只要i与used[i]在同一个集合,那么该序列就可以通过交换达成目标,反之只要有一个数不满足i与used[i]在同一个集合,则该序列无法达成目的
【参考代码】

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int fa[N], used[N], n, a;
int findd(int x) {
    if (x == fa[x])
        return x;

    return fa[x] = findd(fa[x]);
}
int main() {
    cin >> n;

    for (int i = 1; i <= n; i ++) {
        cin >> a;
        fa[i] = i;
        used[a] = i;
    }

    for (int i = 1; i <= n; i ++) {
        cin >> a;

        if (i + a <= n)
            fa[findd(i)] = findd(i + a);

        if (i - a >= 1)
            fa[findd(i)] = findd(i - a);
    }

    for (int i = 1; i <= n; i ++) {
        if (findd(used[i]) != findd(i)) {
            puts("NO");
            return 0;
        }
    }

    puts("YES");
}

D. Flag
【题目大意】这样的属于三色旗
在这里插入图片描述
这样的不是三色旗
在这里插入图片描述
给定n×m的矩阵,由“ a ”到“ z ”的小写英文字母组成。相同的字母对应相同的颜色,不同的字母对应不同的颜色,求一共有多少个三色旗
【思路】(乱搞)描述可能不仅详细,关键还是自己动手实践一下,基本思路算是一遍一遍数出来,考试的时候考虑的组成三色旗的可以连起来,没考虑三色旗的每个颜色都得一样,后来也没发现错误。1000的数据刚开始想借鉴射命丸文(codevs 1373)的思路,发现做不动,然后继续思考,三色旗的限制条件比较大每一层都是一样的颜色,先假设只有3×m的格子,那么只需要判断某个旗子的上一个和上上个是否是不同的即可,对于连续的旗子,第一个旗子+1,第二个相同的旗子+2(本身构成以及和之前所有列构成一个大的),第三个相同的+3依次类推,那么我们扩大到n×m的格子,已知竖着的连续相同旗子无法构成三色旗,但是其数量决定三色旗最大为多少,我设置s表示每个旗子是第几个连续相同的旗子一方面可以快速找到第一个不同的旗子,另一方面可以判断这两个不同的旗子最大能够组成三色旗的大小(射上面a个,下面b个,若上面比下面大,则大小可能为3b,若下面比上面大,则无法构成三色旗,需要重新组旗子)由于旗子颜色有3种,引进参数id,id表示该位置最大为第几色旗,同时id与3取min,因此s的更新方式为:若该数与上面相等则s+1,若不等则s = 1,id的更新方式为若s与第i - s位相等则id + 1,若s小于第i - s位则id = 2,若s与大于i - s位则id = 1最后数id等于3的个数,数的方法和3×m的格子一样
【参考代码】

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
long long ans;
long long n,m,ji;
char c,ma[N][N];
struct zt
{
	int s,id;
}num[N][N];
bool check(int i,int j)
{
	if(num[i][j - 1].s != num[i][j].s) return false;
	if(num[i][j - 1].id != 3) return false;
	if(ma[i][j] != ma[i][j - 1]) return false;
	if(ma[i - num[i][j].s][j] != ma[i - num[i][j].s][j - 1]) return false;
	if(ma[i - num[i][j].s - num[i][j].s][j] != ma[i - num[i][j].s - num[i][j].s][j - 1]) return false;
	return true;
}
int main()
{
	cin>>n>>m;
	for(int i = 1;i <= n;i ++)
		for(int j = 1;j <= m;j ++)
		{
			c = getchar();
			while(c < 'a'||c > 'z') c = getchar();
			ma[i][j] = c;
			if(i == 1)
			{
				num[i][j].s = 1;
				num[i][j].id = 1;
			}
			else
			{
				if(ma[i][j] == ma[i - 1][j])
				{
					num[i][j].s = num[i - 1][j].s + 1;
					if(i - num[i][j].s <= 0) num[i][j].id = 1;
					else if(num[i - num[i][j].s][j].s < num[i][j].s) num[i][j].id = 1;
					else if(num[i - num[i][j].s][j].s == num[i][j].s) num[i][j].id = min(num[i - num[i][j].s][j].id + 1,3);
					else if(num[i - num[i][j].s][j].s > num[i][j].s) num[i][j].id = 2;
				}
				else
				{
					num[i][j].s = 1;
					if(num[i - 1][j].s == num[i][j].s) num[i][j].id = min(num[i - 1][j].id + 1,3);
					else if(num[i - 1][j].s > num[i][j].s) num[i][j].id = 2;
				}
			}
		}
	ji = 1;
	for(int i = 1;i <= n;i ++)
		for(int j = 1;j <= m;j ++)
			if(num[i][j].id == 3)
			{
				if(check(i,j)) ji ++;
				else ji = 1;
				ans += ji;
			}	
	cout<<ans;
}

E. Kleofáš and the n-thlon
【题目大意】有m个人进行n场比赛,你知道你的每一场比赛的排名(1~m之间),求你的期望名次
【思路】(概率DP+差分数组)除你之外,每个人每场比赛的取得不同名次的概率是相同的,我们想直接求自己的期望排名是很困难的,但是我们可以求得分为t的期望人数,比如有5个人,第一场我们的得分为3分,第0场比赛各个得分的期望人数是4 0 0 0 0 第一场比赛各个得分的期望人数是0 1 1 0 1 1,显然期望得分为0的不见了,对应的除了我们获得的3分之外还有1,2,4,5这4种得分,所以可以看出(稍微动动脑子),假设前一次比赛分数为j的有t人,则对于除了我们所得到的分数a[i]之外在1~m之前都加了t/(m - 1)(显然对于当前得分的所有人都可能获得除a[i]之外的1到m的任意值),这样我们的复杂度变成了O(n^2 m^2 )显然过不了,我们用差分数组优化一层m即可(有的也会用树状数组或BIT进行优化)
【参考代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105;
const int M = 1005;
int n,m,sum,a[N];
double dp[N * M],cha[N * M];
int main()
{
    while(scanf("%d%d",&n,&m) == 2)
    {
        sum = 0;
        for(int i = 1;i <= n;i ++)
		{
			scanf("%d",&a[i]);
			sum += a[i];
		}
        if(m == 1)
        {
            printf("1\n");
            continue;
        }
        memset(dp,0,sizeof(dp));
        dp[0] = m - 1;//0场比赛的时候,所有人的期望分数都为0  
        for(int i = 1;i <= n;i ++)
        {
            memset(cha,0,sizeof(cha));//清空差分数组  
            for(int j = 0;j <= n * m;j ++)
            {
                double p = dp[j] / (m - 1);
				//dp为滚动数组,表示之前得分为j的期望人数,由于每个人的得分是相同的p表示每种得分期望加的人 
                cha[j + 1] += p;
				cha[j + m + 1] -= p;
				//从分数为j+1到分数为j + m均加上p 
                cha[j + a[i]] -= p;
				cha[j + a[i] + 1] += p;
				//由于a[i]是我们自己的,不会被其他人得到,故删除j + a[i]的得分期望加的人数 
            }
            dp[0] = cha[0]; //0单独处理  
            for(int j = 1;j <= n * m;j ++)
			{
				cha[j] += cha[j - 1];//计算查分数组得到原数组  
				dp[j] = cha[j];//更新dp 
			}
        }
        double ans = 0;
        for(int i = 0;i < sum;i ++)
			ans += dp[i];
		//dp[i]表示分数为i的期望人数  
        printf("%.12f\n",ans+1.0);
    }
}                                              
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值