• [SinGuaRiTy] 2017-07-24 NOIP2015 模拟赛


    【SinGuLaRiTy-1030】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

    对于所有题目: Time Limit: 1s | Memory Limit: 256MB

                   

    旅行 (travel)

    题目描述

    Mr_H 旗下的 n 个OIer 坐船外出旅行!
    但是他们只有一艘船,虽然船能装下全部的Oier,但太拥挤将会影响众OIer 的心情,所以Mr_H决定选择一部分Oier 去。我们假设,每个人单独坐船的快乐程度是Ci,而船上每多一个人,他的快乐程度会减去Di。
    现在你的任务是帮助Mr_H 计算,选择那些人,才能使船上所有人的快乐程度之和达到最大。

    输入

    第 1 行是一个整数n,表示OIer 的人数;
    第 2 行有n 个整数,第i 个整数表示第i 个人人单独坐船的快乐程度Ci(1<=Ci<=10000);
    第 3 行有n 个整数,第i 个整数表示每多1 人,第i 个人快乐程度的下降值Di(1<=Di<=10)。

    输出

    第 1 行一个整数,是最大的快乐程度之和;
    第 2 行一个整数,是最大的快乐程度之和所对应的汽艇上的人数(若有多种方案,则输出人数最多的)。

    样例数据

    样例输入 样例输出

    6
    10 10 10 10 10 9
    2 2 2 2 2 3

    18
    3

    <数据范围>

    对于30%的数据,n<=20;
    对于100%的数据,n<=1000。

    解析

    很明显,直接枚举是没有必要的。我们可以枚举人数 K,然后算出在这种情况下每个人的快乐程度。然后贪心地取前K 大的数,这样就得到了 O(n^2*logn) 的算法。

    Code

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    
    #define MAXN 1010
    #define INF 2000000000
    
    using namespace std;
    
    int n,d[MAXN][MAXN];
    int ans=-INF;
    
    void init()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        scanf("%d",&d[1][i]);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            for(int j=2;j<=n;j++)
            d[j][i]=d[j-1][i]-x;
        }
    }
    
    int main()
    {
        init();
        int s;
        for(int i=1;i<=n;i++)
        {
            sort(d[i]+1,d[i]+1+n);
            if(d[i][n-i+1]<=0)
                break;
            int x=0;
            for(int j=0;j<i;j++)
                x+=d[i][n-j];
            if(ans<=x)
            {
                ans=x;
                s=i;
            }
        }
        printf("%d
    %d",ans,s);
        return 0;
    }

    数据 (data)

    题目描述

    Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:
    试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这n 个待排序的数。
    例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有3 个数(4,2,-1),第二组有4个数(1,2,3,4)。可是现在Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有2 个数(1,9),第二组数据有3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!
    现在 Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写一个程序,计算最少需要多少步才能将数据改得合法。

    输入

    第一行一个整数m,表示Mr_H 做的输入数据包含的整数个数。第二行包含m 个整数a[i],每个整数的绝对值不超过10000。

    输出

    一个整数,表示把数据修改为合法的情况下,最少需要多少步。

    样例数据

    样例输入1 样例输出1

    4
    1 9 3 2

    2
    样例输入2 样例输出2

    10
    4 4 3 5 0 -4 -2 -1 3 5

    3

    <数据范围>

    对于 20%的数据,m<=10, |a[i]|<=5;
    对于60%的数据,m<=5000, |a[i]|<=10000
    对于100%的数据,m<=100000, |a[i]|<=10000

    解析

    先来谈谈图论的解法,简单的说就是连边然后找最短路:若a[i]>0,那么就说明这个数可以用来表示数列的长度,我们就从a[i]连一条边到下一个数列i+a[i]+1,并设定其权值为0——因为这里实际上不需要耗费任何操作。如果i+a[i]+1超过了n怎么办呢?我们就在任意两个相邻的数间连一条权值为1的边——这里的左移右移就相当于加和减的操作,是需要消耗布数的。注意:第一个点和第二个点不能连,会出毒的。另外,若a[1]<0,我们显然要将其变为正数,于是这样的话我们需要在ans后面直接加上a[1],即使a[1]变为正数的步数。至于最后的答案嘛,直接跑1~n的最短路就行了。

    不过,这么玄(zhèng) 学(cháng) 的东西怎么能是标准解法呢?要看DP+优化的同学还是直接 [下载PDF题解] 好了。

    Code

    #include<cstdio>
    #include<queue>
    #include<vector>
    #include<iostream>
    
    #define need 110003
    #define inf 1e9
    
    using namespace std;
    
    int n,nn;
    
    inline void Read(int &d)
    {
        char t=getchar();bool mark=false;
        while(t<'0'||t>'9') {if(t=='-') mark=true;t=getchar();}
        for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
        if(mark) d=-d;
    }
    
    struct bian
    {
        int la,en,len;
    };
    vector<bian> w;
    
    int tot,fi[need];
    
    void add(const int &a,const int &b,const int &c)
    {
        tot++;
        w.push_back((bian){fi[a],b,c});
        fi[a]=tot;
    }
    
    int s,e;
    
    struct fy
    {
        int dis,id;
        bool operator< (const fy &b) const
        {
            return dis>b.dis;
        }
    };
    priority_queue<fy> q;
    
    bool getans[need];
    int dis[need];
    
    void dijkstra(int s)
    {
        for(int i=1;i<=nn;i++)
        {
            dis[i]=inf;
            getans[i]=false;
        }
        q.push((fy){0,s});
        dis[s]=0;
        int x,y,t;
        while(!q.empty())
        {
            while(!q.empty()&&getans[q.top().id])
                q.pop();
            x=q.top().id;
            if(x==e)
                return ;
            getans[x]=true;
            q.pop();
            for(t=fi[x];t;t=w[t].la)
            {
                y=w[t].en;
                if(getans[y])
                    continue;
                if(dis[y]>dis[x]+w[t].len)
                {
                    dis[y]=dis[x]+w[t].len;
                    q.push((fy){dis[y],y});
                }
            }
        }
    }
    
    int main()
    {
        w.resize(1);
        int n;
        scanf("%d",&n);
        int dans=0;
        int a;
        Read(a);
        if(a<0)
            dans=-a,add(1,2,0);
        else
            add(1,a+2,0);
        nn=max(nn,a+2);
        for(int i=2,a;i<=n;i++)
        {
            Read(a);
            if(a>=0)
                add(i,i+a+1,0);
            nn=max(nn,i+a+1);
            add(i,i+1,1);
            add(i+1,i,1);
        }
        for(int i=n+1;i<=nn;i++)
        {
            add(i,i+1,1);
            add(i+1,i,1);
        }
        s=1;
        e=n+1;
        dijkstra(s);
        cout<<dis[e]+dans;
        return 0;
    }

    <DP+优化 Code>

    #include<iostream>//乱搞
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int Max = 100000;
    const int INF = 0x3f3f3f3f;
    
    struct node{
        int val, pos;
        node(){}
        node(int a, int b){ val = a, pos = b;}
        bool operator < (const node & X) const{
            return val > X.val;
        }
    };
    
    int N;
    int A[Max + 5], Dp[Max + 5];
    priority_queue<node>Q;
    
    bool getint(int & num){
        char c; int flg = 1;    num = 0;
        while((c = getchar()) < '0' || c > '9'){
            if(c == '-')    flg = -1;
            if(c == -1) return 0;
        }
        while(c >= '0' && c <= '9' ){
            num = num * 10 + c - 48;
            if((c = getchar()) == -1)   return 0;
        }
        num *= flg;
        return 1;
    }
    
    int main(){
        getint(N);
        for(int i = 1; i <= N; ++ i)    getint(A[i - 1]), A[i - 1] += i;
        Q.push(node(A[0], A[0]));
        memset(Dp, 0x3f, sizeof Dp );
        int minnum = INF;
        for(int i = 1; i <= N; ++ i){
            while(! Q.empty() && Q.top().pos < i){
                minnum = min(minnum, Q.top().val - 2 * Q.top().pos);
                Q.pop();
            }
            Dp[i] = min(Dp[i], minnum + i);
            if(! Q.empty()) Dp[i] = min(Dp[i], Q.top().val - i);
            Q.push(node(Dp[i] + A[i], A[i]));
        }
        printf("%d
    ", Dp[N]);
        return 0;
    }
    View Code

    业务 (business)

    题目描述

    Mr_H 谋得一份兼职——货车司机,从此以后他将会开着货车穿行在C 国的各大城市之间。
    C 国中有n 座城市(编号为1~n),并且有m 条双向公路,每条公路连接两座不同的城市。货车从任意一座城市出发都可以抵达任意另一座城市。在每条公路上,都有一个收费站,通过的车辆需要交纳一定过路费。可能有多条公路连接相同的两座城市。
    为了增加财政收入,C 国还在每座城市也设置了收费站。并且规定,车辆从一座城市到另一座城市的费用是,所经过公路费用和,加上所经过的城市中费用的次大值(这里的次大可以和最大相同,但是城市不同)。
    现在Mr_H 告诉你今年k 次业务运送货物的起点、终点城市列表,请你帮忙计算,每次业务需要交纳的最低过路费。

    输入

    第一行包含三个用一个空格隔开的整数:n,m,k。其意义如题目描述。
    第 2 到第 n+1 行:第i+1 行包含一个单独的整数 c(1<=c<=100000),表示城市i 的费用。
    接下来的m 行,每行包含三个整数a,b,w,表示一条公路连接城市a 和城市b(1<=a,b<=n),其过路费为w(1<=w<=100000)。
    最后的 k 行,每行包含两个整数:s,t,表示一次业务的起点和终点(1<=s,t<=n 且 s!=t)。

    输出

    共k 行,每行一个整数,表示从城市s 到t 的最少过路费。

    样例数据

    样例输入 样例输出

    5 7 3
    2
    5
    3
    3
    4
    1 2 3
    1 3 2
    2 5 3
    5 3 1
    5 4 1
    2 4 3
    3 4 4
    1 3
    1 4
    2 3

    4
    7
    8

     

     

     

     

     

     

     

     

     

     

     

     

    <样例解释>

    包含 5 个城市的样例图形如下:

    ●城市1 到城市3 的道路的“边过路费”为 2,“点过路费”为 2(城市2 的费用为次大)。所以总的花费为 2+2=4 。
    ●要从城市1 走到城市4 ,可以从城市1 走到城市3,再走到城市5,最后到达城市4 。如果这么走的话,需要的“边过路费”为 2+1+1=4,需要的点过路费为 3(城市3 或城市4 的点过路费次大),所以总的花费为 4+3=7。
    ●从城市2 走到城市3 的最佳路径是从城市2 出发,抵达城市5,最后到达城市3,这么走的话,边过路费为 3+1=4,点过路费为4,总花费为 4+4=8。

    <数据范围>

    对于 20%的数据,n<=10, m<=20
    对于50%的数据,n<=100, m<=5000
    对于100%的数据,1<=n<=250 , 1<=m<=10000 , 1<=k<=10000 ,其中有50%的数据点权没有重复。

    解析

    (考试的时候用的DFS,没D出来)

    令:ans[x][y]=从x 到y 的最小过路费!计算ans[x][y]可以用DIJKstra 算法或SPFA 算法!
    令:dist[i][0]=从k 出发到i 点经过的点费用最大值为a[k]的情况的最小边费用和;dist[i][1]=从k 出发到i 点经过的点费用次大值为a[k]的情况的最小边费用和
    于是有: ans[x][y]=min{dist[x][0]+dist[y][1],dist[x][1]+dist[y][0]}+a[k]}其中 1<=k<=n
    计算出ans[x][y]后,回答询问就很简单了。
    时间复杂度 O(n2logm+k) (DIJKstra 堆优化后的时间复杂度)

    Code

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<queue>
    
    #define MAXN 250
    #define MAXM
    #define DIST(x)(tmp[x.pos][x.hm])
    #define VIS(x)(vis[x.pos][x.hm])
    #define INF 0x3f3f3f3f
    
    using namespace std;
    
    typedef long long int LL;
    
    int getint()
    {
        int rn=0;
        char c=getchar();
        while(c<'0'||'9'<c)
            c=getchar();
        while('0'<=c&&c<='9')
        {
            rn=rn*10+c-'0';
            c=getchar();
        }
        return rn;
    }
    
    int G[MAXN+10][MAXN+10];
    int Val[MAXN+10];
    int dist[MAXN+10][MAXN+10];
    int N,M,K;
    
    int tmp[MAXN+10][2];
    bool vis[MAXN+10][2];
    
    struct point
    {
        int pos,w;
        bool hm;
        point(){}
        point(int a,bool b,int c){pos=a,hm=b,w=c;}
        bool operator < (const point &a)const
        {
            return w>a.w;
        }
    };
    
    priority_queue<point>que;
    void insert(point newed)
    {
        if(DIST(newed)<newed.w)
            return;
        DIST(newed)=newed.w;
        que.push(newed);
    }
    
    void Dijkstra(int sc)
    {
        memset(tmp,0x3f,sizeof(tmp));
        memset(vis,0,sizeof(vis));
        insert(point(sc,0,0));
        point now,newed;
        while(!que.empty())
        {
            now=que.top();
            que.pop();
            if(VIS(now))
                continue;
            else
                VIS(now)=1;
            for(int v=1;v<=N;++v)
                if(G[now.pos][v])
                {
                    newed.pos=v;
                    newed.hm=now.hm;
                    newed.w=DIST(now)+G[now.pos][v];
                    if(Val[v]>Val[sc])
                    {
                        if(now.hm)
                            continue;
                        else
                        {
                            newed.hm=1;
                            insert(newed);
                        }
                    }
                    else if(Val[v]==Val[sc])
                    {
                        if(v==sc)
                            insert(newed);
                        else
                        {
                            insert(newed);
                            newed.hm=1;
                            insert(newed);
                        }
                    }
                    else
                        insert(newed);
                }
        }
    }
    
    int main()
    {
        scanf("%d%d%d",&N,&M,&K);
        int i,j;
        for(i=1;i<=N;++i)
            scanf("%d",&Val[i]);
        int a,b,c;
        for(i=1;i<=M;++i)
        {
            scanf("%d%d%d",&a,&b,&c);
            G[a][b]=(!G[a][b]||G[a][b]>c?c:G[a][b]);
            G[b][a]=(!G[b][a]||G[b][a]>c?c:G[b][a]);
        }
        memset(dist,0x3f,sizeof(dist));
        for(int sc=1;sc<=N;++sc)
        {
            Dijkstra(sc);
    
            for(i=1;i<=N;++i)
                for(j=1;j<=N;++j)
                    dist[i][j]=min(dist[i][j],min(tmp[i][0]+tmp[j][1],tmp[i][1]+tmp[j][0])+Val[sc]);
        }
        for(i=1;i<=K;++i)
        {
            scanf("%d%d",&a,&b);
            printf("%d
    ",dist[a][b]);
        }
        return 0;
    }

    Time: 2017-07-24

  • 相关阅读:
    多线程等待
    多线程多进程
    Django中的 返回json对象的方式
    爬虫之 单线程+多任务异步协程
    python 调用github的api,呈现python的受欢迎的程度
    爬虫之线程池
    爬虫之代理和cookie的处理
    pip 安装报错
    git 新建仓库第一次提交
    ansible的剧本
  • 原文地址:https://www.cnblogs.com/SinGuLaRiTy2001/p/7229673.html
Copyright © 2020-2023  润新知