zoukankan      html  css  js  c++  java
  • 【NOIP模拟赛】【数学真奇妙】【递推】旅行者问题

    旅行者问题

    【问题描述】

    lahub是一个旅行者的粉丝,他想成为一个真正的旅行者,所以他计划开始一段旅行。lahub想去参观n个目的地(都在一条直道上)。lahub在起点开始他的旅行。第i个目的地和起点的距离为ai千米(ai为非负整数)。不存在两个目的地和起点的距离相同。

        从第i个目的地走到第j个目的地所走的路程为 |ai-aj|千米。我们把参观n个目的地的顺序称作一次旅行lahub可以参观他想要参观的任意顺序,但是每个目的地有且只能被参观一次(参观顺序为n的排列)。

        lahub把所有可能的旅行都写在一张纸上,并且记下每个旅行所要走的路程。他对所有旅行的路程之和的平均值感兴趣。但是他觉得计算太枯燥了,所以就向你寻求帮助。

    【输入格式】

    第一行一个正整数n

        第二行n个非负整数a1,a2,....,an1≤ai≤10^7)。

    【输出格式】

    两个整数答案用最简分数形式输出,第一个为分子,第二个为分母。

    【输入样例】

    3

    2 3 5

    【输出样例】

    22 3

    【样例提示

    样例有6种可能的旅行:

    [2, 3, 5]: 旅行的路程:|2 – 0| + |3 – 2| + |5 – 3| = 5;

    [2, 5, 3]: |2 – 0| + |5 – 2| + |3 – 5| = 7;

    [3, 2, 5]: |3 – 0| + |2 – 3| + |5 – 2| = 7;

    [3, 5, 2]: |3 – 0| + |5 – 3| + |2 – 5| = 8;

    [5, 2, 3]: |5 – 0| + |2 – 5| + |3 – 2| = 9;

    [5, 3, 2]: |5 – 0| + |3 – 5| + |2 – 3| = 8.

    答案为 1/6 * 5+7+7+8+9+8=44/6=22/3

    【数据范围】

    30% n<=10

    50% n<=1000

    100% n<=100000

    【Solution】

      虽然A了但还是很想吐槽,这题竟然是个数学问题...

      首先先枚举每条可能的第一条路。对于某一种可能的第一条路,其余点的排列方式有(n-1)!种,所以这条路对所有以这条路为起点的路径的贡献和为(n-1)*dist[i]。

      对于某一条路i--j,其余(n-2)个点的排列方式有(n-2)种,而这条路可以插入的位置有n-1种,所以这条路对所有带这条路的路径的贡献和为(n-2)!*(n-1)*abs(dist[i]-dist[j])=(n-1)!*abs(dist[i]-dist[j])。

      根据以上两种情况,路径和=

      现在的问题就是怎么求加号后面的那一坨了。

      我们将dist从大到小排序,排序后忽略的情况在最后求出路径和乘二即可。用S[i]表示到i的所有路程和。我们发现:

      S2 = dist[1] - dist[2]

      S3 = dist[1] - dist[3] + dist[2] - dist[3] = (dist[1] - dist[2] + dist[2] - a3) + dist[2] - dist[3] = S2 + 2 *(dist[2] - dist[3]

      S4 = dist[1] - dist[4] + dist[2] - dist[4] + dist[3] - dist[4] = (dist[1] - dist[3] + dist[3] - dist[4]) + (dist[2] - dist[3] + dist[3] - dist[4]) + (dist[3] - dist[4]) = S3 + 3 * (dist[3] - dist[4])

      不难看出,S[i]可以由递推求出,递推式为S[i]=S[i-1]+(i-1)*(dist[i-1]-dist[i])。虽然式子里还要乘个(n-1)!,但是由于分子为n!,约一下分母上的(n-1)!消掉了,分子变成n,求个gcd分子分母一除输出答案就行了。

      AC代码:

     1 #include <cstdio>
     2 #include <algorithm>
     3 using namespace std;
     4 int N;
     5 int dist[100010];
     6 long long sum,ans;
     7 long long recs[100010];
     8 void input(){
     9     scanf("%d",&N);
    10     for(int i=1;i<=N;++i) scanf("%d",&dist[i]);
    11 }
    12 int cmp(const int a,const int b){
    13     return a>b;
    14 }
    15 long long gcd(long long x,int y){
    16     long long ys=1;
    17     while(ys!=0){
    18         ys=x%y;
    19         x=y;
    20         y=ys;
    21     }
    22     return x;
    23 }
    24 int main(){
    25     input();
    26     for(int i=1;i<=N;++i) sum+=dist[i];
    27     sort(dist+1,dist+1+N,cmp); recs[2]=dist[1]-dist[2];
    28     for(int i=3;i<=N;++i) 
    29         recs[i]=recs[i-1]+(i-1)*(dist[i-1]-dist[i]);
    30     for(int i=2;i<=N;++i) ans+=recs[i];
    31     ans=ans*2+sum;
    32     long long k=gcd(ans,N);
    33     printf("%I64d %I64d",ans/k,N/k);
    34     return 0;
    35 }
  • 相关阅读:
    Android学习记录(4)—在java中学习多线程下载的基本原理和基本用法①
    Android之Notification的多种用法
    通过学习制作长微博工具来了解水印的制作,及EditText中的内容在图片中换行显示
    通过短信窃听器来讲解内容提供者,内容观察者,以及无界面后台运行服务,开机启动和杀死服务后重新启动
    通过重写ViewGroup学习onMeasure()和onLayout()方法
    Android学习记录(3)—Android中ContentProvider的基本原理学习总结
    Android学习记录(2)—Android中数据库的常见操作
    Android学习记录(1)—Android中XML文件的序列化生成与解析
    Android之判断设备网络连接状态,并判断连接方式
    Android获取SD卡总容量,可用大小,机身内存总容量及可用大小的系统方法
  • 原文地址:https://www.cnblogs.com/reddest/p/5992018.html
Copyright © 2011-2022 走看看