PTA 感染源在哪里 C语言代码

这题吧,我的评价是挺有意思。第一时间看到这题我还觉得非C++不可,后来仔细想了想发现C语言又不是不能做,真写起来发现也不是太难,写完后发现比运行C++都快(快一倍多呢)。如果你想提升一下自己,那我建议你再对这道题进行一段时间的思考。

我在这里先解释下我设置的变量,以便后续的讲解。(当然可以倒回来看)

int pet[1000][1001];//一个人都去过哪些城市,其中pet[][1000]存放这个人去了多少座城市(可有重复)
double pot[1000];//每个城市的得分

这两个是全局变量,原因有二:

1、pet太大,放函数里可能崩溃;

2、这两个都需要全初始化为0,放外面可以少两句memset(doge)。

int cpe = 0, cpo = 0;
char pe[1000][11], po[1000][11], t1[11], t2[11];
int n, t, cnt;
int uni[1000], order[1000];

 注:我将people缩为pe,point缩为po。

cpe和cpo的意思分别是总人数和总城市数。

pe和po用于存放人名和城市名(不重复)。

n是输入数据数。

t是一块砖,哪里需要哪里搬。

cnt用于计算一个人去过多少地方(不重复)。

uni用于判断一个人去的地方是否已经去过。

order用于排序后存储输出顺序。

放弃思考的话就听听我的思路吧。我将这题分为5部分:输入,转义,积分,排序,输出。

这题最让人头疼的莫过于它的城市和人员是字符串,想一想,如果城市和人员的命名是从0到n-1的数字,这题是不是就相对好说了?因而,我们的第一步就是将字符串改命名为0到n-1的数字,我建议你在理解这一步后,进行自己的思考去完成这一题,毕竟后面的步骤不算过分。

int mapstringlike(char a[][11], char b[], int *c)
{
    for (int i = 0; i < *c; i++)
        if(!strcmp(a[i],b))
            return i;
    strcpy(a[(*c)++], b);
    return *c - 1;
}

顺便给出使用此函数时的代码。

while(n--)
{
scanf("%s%s", t1, t2);
t = mapstringlike(pe, t1, &cpe);
pet[t][pet[t][1000]++] = mapstringlike(po, t2, &cpo);
}

解释一下函数的三个参数:

a:不重复的人名/城市名。

b:刚输入的人名/城市名。

C:已知的不重复的人名/城市名数。(用指针的原因是该值需要在函数中修改)

那么这个函数的目的很明显:将字符串组中的字符串一一对比刚输入进的字符串,如果刚输入进的字符串与任一字符串组中的字符串相同,就返回一个数字:标志着这个字符串用这个数字代替。

如果该字符串不与任何已知的字符串相同,就让不重复的人名/城市名数+1,并且存储这个字符串,然后返回一个数字:标志着这个字符串用这个数字代替。

这个函数的返回值的具体意思是:与b字符串同名的字符串在a中第一次出现时前面已出现多少不重名的字符串。当然你不用记它的意思,你只用知道这个函数的目的是将同名的字符串转化为同名的数字,不同的字符串转化为不同的数字。

由此我们已经实现将字符串转化为数字。接下来就相对简单了,在开始之前,我仍建议你尝试独立完成。

下一步是为每个城市记分。代码如下

    for (int i = 0; i < cpe; i++)
    {
        memset(uni, 0, sizeof(uni));
        cnt = 0;
        for (int j = 0; j < pet[i][1000]; j++)
        {
            if(!uni[pet[i][j]])
                uni[pet[i][j]] = ++cnt;//反正只要不是0就是真
            else
                pet[i][j] = -1;
        }
        for (int j = 0; j < pet[i][1000]; j++)
            if (pet[i][j] != -1)
                pot[pet[i][j]] += 1.0 / cnt;
    }

uni用于存放这个城市是否已经去过。如果城市已经去过,则在后续的记分中应忽略二次出现的此城市。cnt用于存放有效城市数,以便于后续记分。

记分之后,就是排序了,代码如下

    for (int i = 0; i < cpo; i++)
        order[i] = i;
    for (int i = 0; i < cpo; i++)
    {
        t = i;
        for (int j = i + 1; j < cpo; j++)
            if (pot[order[t]] * 1e4 < pot[order[j]] * 1e4 + strcmp(po[order[t]], po[order[j]]))
            //strcmp可能在某些编译器上显示ASCII差,总之严谨的话不要学我,我这只是在减少代码长度(doge)
                t = j;
        if(t!=i)
        {
            order[i] += order[t];
            order[t] = order[i] - order[t];
            order[i] -= order[t];
        }
    }

采用选择排序。交换order而不是交换pot和po的原因也是为了减少代码长度(doge)。

*1e4的原因是为了实现先比较得分再比较字典序,毕竟strcmp才+1或-1了,要想有影响得分差要小于千分之一,很少会出这么离谱的数据吧。当然你要保险可以整个1e6什么的。

最后就剩输出了,输出完程序结束。

    for (int i = 0; i < cpo; i++)
        printf("%s\n", po[order[i]]);

看到这里,我想大概都懂了,我在最后给出整个程序,以便大家更好地理解。

#include<stdio.h>
#include<string.h>
int pet[1000][1001];
double pot[1000];
int mapstringlike(char a[][11], char b[], int *c)
{
    for (int i = 0; i < *c; i++)
        if(!strcmp(a[i],b))
            return i;
    strcpy(a[(*c)++], b);
    return *c - 1;
}
int main()
{
    int cpe = 0, cpo = 0;
    char pe[1000][11], po[1000][11], t1[11], t2[11];
    int n, t, cnt;
    int uni[1000], order[1000];
    scanf("%d", &n);
    while(n--)
    {
        scanf("%s%s", t1, t2);
        t = mapstringlike(pe, t1, &cpe);
        pet[t][pet[t][1000]++] = mapstringlike(po, t2, &cpo);
    }
    for (int i = 0; i < cpe; i++)
    {
        memset(uni, 0, sizeof(uni));
        cnt = 0;
        for (int j = 0; j < pet[i][1000]; j++)
        {
            if(!uni[pet[i][j]])
                uni[pet[i][j]] = ++cnt;
            else
                pet[i][j] = -1;
        }
        for (int j = 0; j < pet[i][1000]; j++)
            if (pet[i][j] != -1)
                pot[pet[i][j]] += 1.0 / cnt;
    }
    for (int i = 0; i < cpo; i++)
        order[i] = i;
    for (int i = 0; i < cpo; i++)
    {
        t = i;
        for (int j = i + 1; j < cpo; j++)
            if (pot[order[t]] * 1e4 < pot[order[j]] * 1e4 + strcmp(po[order[t]], po[order[j]]))
                t = j;
        if(t!=i)
        {
            order[i] += order[t];
            order[t] = order[i] - order[t];
            order[i] -= order[t];
        }
    }
    for (int i = 0; i < cpo; i++)
        printf("%s\n", po[order[i]]);
    return 0;
}//我不怕你直接交这个的,我自己交的是直接用map<string,double>的(doge)

祝大家有一个好的暑假,在暑假期间也要认真学习哦(doge)。

看都看完了,不关个注再走吗(doge)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值