第七次上机

这次上机感觉没上次那么难,没什么稀奇古怪的算法;
但是d题回来研究了好久,做了大概八个小时终于AC了orz。
还是要继续学习啊!

A 崔哥的信仰

时间限制:1000ms内存限制:65536KB

总通过人数:175 总提交人数:176

评测记录:{A182701}

解题思路:

将每个物品的参数储存到数组a中,下标由1n

再将数组a用sort函数排序,在输出a[n]和a[n-1]即可。

参考代码:

#include

#include

#include

#include

using namespace std;

int main(){

    int n;

    scanf("%d",&n);

    long long a[20000];

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

        scanf("%lld",&a[i]);

    }

    sort(a+1, a+1+n);

    printf("%lld %lld\n",a[n],a[n-1]);

}

 

 

 

 

B  王助教掷骰子

时间限制:1000ms内存限制:65536KB

总通过人数:22 总提交人数:147

评测记录:{B184164}

解题思路:

 

这道题我使用了递推的方法:

 

将骰子的六个面分成两类:初始面+它的对面,记为P面  和 其余的四个侧面,记为Q面;

易知第n次初始面和它的对面朝上的概率相等; 其余四个侧面朝上的概率相等;

P(n)表示第n次滚动后P面朝上的概率,Q(n)表示第n次滚动后Q面朝上的概率。

 

只有当第n-1次滚动后Q面朝上,才可能下一次滚动后P面朝上,并且概率为1/2

 
 

 

于是有:

P(n) = Q(n-1)/2;--------------[1]

又因为Q(n) + P(n) = 1;

故:

Q(n) = 1 - P(n);---------------[2]

 

[1]  [2]式构成了一个递推关系,易知P(1) = 0,Q(1) = 1,便能递推出第k次滚动后的PQ,接着P/2便得到初识面和tade对面朝上的概率,Q/4便得到其余四个侧面朝上的概率。

 

但是,由于k最大为2^31-1,约等于20亿,也就是最多要进行几十亿次计算,肯定会超时,所以还需要优化算法。

 

此时注意到答案只需要保留两位小数(以百分数的形式),而P(14)/2 = 0.166687; Q(14)/4=0.1666565;  P(15)/2=0.166672; Q(15)/4=0.166664....之后P(n)/2Q(n)/4的前四位已经稳定,所以继续求下去结果不会改变,所以程序最多只需要递推14次即可;

 

 

此外,由[1][2]可得Q(n) = 1 - Q(n-1)/2; 即得到了Q(n)的递推公式,由高中知识可得Q(n)的通项公式:

 

   ,便可直接求出PQ,不需要担心超时的问题,强无敌!!

 

参考代码:

法一:

#include

#include

#include

#include

using namespace std;

int main(){

    double p, q, mid;

    double ans[8];

    long k;//每组数据两个整数ik,表示初始面i,滚动k次。

    int i,j;

    while(scanf("%d%ld",&i,&k)==2){

        if(k==0){

            for(int m=1; m<=6; m++){

                if(m!=i)    ans[m]=0.0;

                else ans[m]= 100.0;

            }

        }

        else{

            p = 0.0; q = 1.0;

            for(j=2; j<=k&&j<=14; j++){//j表示第几次滚动

                mid = q;

                q = p + mid / 2.0;

                p = mid / 2.0;

            }

            if(i==1||i==6){

                ans[1] = ans[6] = p / 2.0 * 100;

                ans[2] = ans[3] = ans[4] = ans[5] = q / 4.0* 100;

            }

            else if(i==2||i==5){

                ans[2] = ans[5] = p / 2.0* 100;

                ans[1] = ans[3] = ans[4] = ans[6] = q / 4.0* 100;

            }

            else{

                ans[3] = ans[4] = p / 2.0* 100;

                ans[1] = ans[2] = ans[5] = ans[6] = q / 4.0* 100;

            }

        }

        printf("%.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%%\n",ans[1],ans[2],ans[3],ans[4],ans[5],ans[6]);

    }

}

 

法二:

#include

#include

int main(){

    double p, q, mid;

    double ans[8];

    long k;//每组数据两个整数ik,表示初始面i,滚动k次。

    int i,j;

    while(scanf("%d%ld",&i,&k)==2){

        if(k==0){

            for(int m=1; m<=6; m++){

                if(m!=i)    ans[m]=0.0;

                else ans[m]= 100.0;

            }

        }

        else{

            q = (1.0/3.0)*pow(-0.5,k-1)+2.0/3.0;

            p=1-q;

            if(i==1||i==6){

                ans[1] = ans[6] = p / 2.0 * 100;

                ans[2] = ans[3] = ans[4] = ans[5] = q / 4.0* 100;

            }

            else if(i==2||i==5){

                ans[2] = ans[5] = p / 2.0* 100;

                ans[1] = ans[3] = ans[4] = ans[6] = q / 4.0* 100;

            }

            else{

                ans[3] = ans[4] = p / 2.0* 100;

                ans[1] = ans[2] = ans[5] = ans[6] = q / 4.0* 100;

            }

        }

        printf("%.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%%\n",ans[1],ans[2],ans[3],ans[4],ans[5],ans[6]);

    }

}

 


C DH分组员

时间限制:1000ms内存限制:65536KB

总通过人数:21 总提交人数:153

评测记录:{C183564}

解题思路:

首先对a1~an求和,得到sum,再分两种情况讨论:

由于sum <= 10^10, 超过了int的范围,故使用long long 声明sum

 

1. sum除不尽K,说明无法平分

直接输出-1;

2. 否则,说明可以平分

ave = sum / K;  //求得均分后每组的人数

由于最后每组的人数都得是ave,于是只要人数不是ave的组都要被合并或拆分;

 

于是,从第一组开始,不断与后一组的合并,且每合并一次操作次数++,直到所得的人数和sum可以除尽ave,这时再把sum均分,操作次数+=(sum/ave-1)即可;(这里我仍然使用了变量sum,而没有定义新的变量)

之后再从下一组开始重复上述操作,直到n组全部完成分配;

 

输出ans(操作次数)。

 

参考代码:

#include

int main(){

    int N,K,i,j,l,ans=0;

    int a[110000];

    long long sum=0,ave;

    scanf("%d%d",&N,&K);

    for(i=1; i<=N; i++){

        scanf("%d",&a[i]);

        sum+=a[i];

    }

    if(sum%K!=0) printf("-1\n");

    else{

        ave = sum / K;

        l=1;

        while(l<=N){

            for(i=l,sum=a[i]; sum%ave!=0; i++,ans++){

                sum+=a[i+1];

            }

            ans+=((sum/ave)-1);

            l=i+1;

        }

        printf("%d\n",ans);

    }

}

D 混乱的辈分关系

时间限制:1000ms内存限制:65536KB

总通过人数:3 总提交人数:46

评测记录:{D188332}

解题思路:

这个题我主要使用了数组,冒泡算法和深搜。

整个程序大致分为以下四步:

 

1.按照题目要求输入----input()

a) ajiaob[][]二维数组储存叫爸爸关系,值为1说明a叫过b爸爸;

b) father数组储存爸爸值,同时用father2数组备份其数据,因为之后的排序会打乱下标;

c) weizhi数组储存每个人所在的位置

d) old数组储存每个人的年龄,同时用old2数组备份

2.对所有人排序,找出爸爸值最大的几个人---max()

a) 使用冒泡算法将所有人按照爸爸值降序排列,注意交换顺序时要将两个人的所有信息(father,old,weizhi值)全部交换

b) 找出和第一名爸爸值相同的所有人,为备份最高的候选人,并储存总候选人数。

3.找出辈分最大的那个人---zhangzhe()

a) 使用compare()函数比较辈分,第一个人与第二个人比较辈分,大的那一个再与下一个人比较,以此类推,找到辈分最大的那个人

b) 输出这个人的位置

c) compare()函数:

i. 使用dfs(p,q)函数判断p是否叫过qq的子孙爸爸;

ii. 记录dfs(p,q)dfs(q,p)的值,同时注意在每次使用dfs()函数前,都要对visit数组清零

iii. dfs(p,q) == 1dfs(q,p)==0,返回q

                       dfs(p,q) == 0dfs(q,p)==1,返回p

          若二者都不满足,用年龄判断辈分,返回辈分较大的那个

       d)    dfs()函数:

深搜,看爸爸关系能否从p推到q

a叫过b爸爸想象成由a指向b的一根箭头

搜索是否由p能走到q

int dfs(int p, int q){

    int i;

    visit[p] = 1;

    if(ajiaob[p][q]==1) return 1;

    for(i=1; i<=n; i++){

        if(visit[i]==0&&ajiaob[p][i]==1){

            if(dfs(i,q)==1) return 1;

        }

    }

    return 0;

}

 

4.按顺序输出这个人的所有儿子---erzi()

a) 通过ajiaob这个数组找到这个人的所有儿子

b) 使用冒泡算法,以爸爸值为第一关键字,年龄为第二关键字,对所有儿子降序排序

   由于之前的排序已经将下标打乱,这次的排序就需要使用备份的数据

c) 按顺序输出所有儿子的位置

反思:

1. 听说这道题使用类会比较简单,但是我还不会,只能用数组来储存所有信息,非常麻烦

2. 数组太多容易搞混,必须要明确每个数组储存了什么信息,有怎样的变化

3. 这次使用的模块化编程效果不错,容易查错,但必须区分清每个函数的作用

 

参考代码:

#include

#include

using namespace std;

int n,m;

int father[11000],father2[11000];

int old[110],old2[110];

int weizhi[110];//[]内表示当前的位置,值表示原来的位置

int ajiaob[110][110];//值为1代表 a叫过b爸爸

int visit[110];

 

void input(void);//按要求输入

void max();//找到爸爸值最大的几个人的位置,储存到maxfather数组中,并返回共有几个人

void zhangzhe(int);//返回找到的辈分最大的那个人的位置

int compare(int p,int q);//比较两个爸爸值相同的人的辈分,返回辈分比较大的那个人的位置

int dfs(int p, int q);//判断p是否叫过q或q的某个子孙(儿子、孙子或曾孙等)爸爸,是返回1,否返回0

void erzi(int);//输入长者的位置(是一开始输入的位置),找到并按顺序输出所有的儿子

 

int main(){

    input();

    max();

}

 

void input(void){

    int a,b;

    scanf("%d%d",&n,&m);

    for(int i=1; i<=m; i++){

        scanf("%d%d",&a,&b);

        if(a!=b){

           father[a]--;

            father[b]++;

            ajiaob[a][b] = 1;

        }

 

    }

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

        scanf("%d",&old[i]);

        old2[i] = old[i];

        father2[i] = father[i];

        weizhi[i] = i;

    }

}

 

void max(){

    int i,j,mid;

    for(i=1; i<=n-1; i++){

        for(j=n; j>=i+1; j--){

            if(father[j]>father[j-1]){

                mid = father[j];

                father[j] = father[j-1];

                father[j-1] = mid;

 

                mid = weizhi[j];

                weizhi[j] = weizhi[j-1];

                weizhi[j-1] = mid;

 

                mid = old[j];

                old[j] = old[j-1];

                old[j-1] = mid;

            }

        }

    }

    for(i=2,j=2; i<=n; i++){

        if(father[i]==father[1]){

            j++;

        }

    }

    j--;

    zhangzhe(j);

}

 

void zhangzhe(int sum){

    int j = weizhi[1];

    for(int i=2; i<=sum; i++){

        j = compare(j,weizhi[i]);

    }

    printf("%d\n",j);

    erzi(j);

}

 

int compare(int p, int q){

    int t,v,i;

    for(i=1; i<=100; i++){

        visit[i] = 0;

    }

    t = dfs(p,q);

    for(i=1; i<=100; i++){

        visit[i] = 0;

    }

    v = dfs(q,p);

    if(t==1&&v==0) return q;

    else if(t==0&&v==1) return p;

    else if(old2[p]>old2[q]) return p;

    else if(old2[q]>old2[p]) return q;

}

 

int dfs(int p, int q){

    int i;

    visit[p] = 1;

    if(ajiaob[p][q]==1) return 1;

    for(i=1; i<=n; i++){

        if(visit[i]==0&&ajiaob[p][i]==1){

            if(dfs(i,q)==1) return 1;

        }

    }

    return 0;

}

 

void erzi(int p){

    int weizhi2[110];

    int father3[11000];

    int old3[110];

    int sum = 1,mid;

    int i, j;

 

    for(i=1; i<=n; i++){//先找到所有儿子

        if(ajiaob[i][p]==1){

            weizhi2[sum] = i;

            father3[sum] = father2[i];

            old3[sum] = old2[i];

            sum++;

        }

    }

    sum--;

    for(i=1; i<=sum-1; i++){//给儿子们排序

        for(j=sum; j>=i+1; j--){

            if(father3[j]>father3[j-1]){

                mid = father3[j];

                father3[j] = father3[j-1];

                father3[j-1] = mid;

 

                mid = weizhi2[j];

                weizhi2[j] = weizhi2[j-1];

                weizhi2[j-1] = mid;

 

                mid = old3[j];

                old3[j] = old3[j-1];

                old3[j-1] = mid;

            }

            else if(father3[j]==father3[j-1]){

                if(old3[j]>old3[j-1]){

                    mid = father3[j];

                    father3[j] = father3[j-1];

                    father3[j-1] = mid;

 

                    mid = weizhi2[j];

                    weizhi2[j] = weizhi2[j-1];

                    weizhi2[j-1] = mid;

 

                    mid = old3[j];

                    old3[j] = old3[j-1];

                    old3[j-1] = mid;

                }

            }

        }

    }

    for(i=1; i<=sum; i++){//按顺序输出所有儿子

        printf("%d ",weizhi2[i]);

    }

}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值