zoukankan      html  css  js  c++  java
  • 2014-5-24 NOIP模拟赛

    Problem 1 护花(flower.cpp/c/pas)

    【题目描述】

    约翰留下他的N(N<=100000)只奶牛上山采木.他离开的时候,她们像往常一样悠闲地在草场里吃草.可是,当他回来的时候,他看到了一幕惨剧:牛们正躲在他的花园里,啃食着他心爱的美丽花朵!为了使接下来花朵的损失最小,约翰赶紧采取行动,把牛们送回牛棚. 牛们从1到N编号.第i只牛所在的位置距离牛棚Ti(1≤Ti≤2000000)分钟的路程,而在约翰开始送她回牛棚之前,她每分钟会啃食Di(1≤Di≤100)朵鲜花.无论多么努力,约翰一次只能送一只牛回棚.而运送第第i只牛事实上需要2Ti分钟,因为来回都需要时间.    写一个程序来决定约翰运送奶牛的顺序,使最终被吞食的花朵数量最小.

    【输入格式】

    1行输入N,之后N行每行输入两个整数Ti和Di

    【输出格式】

    一个整数,表示最小数量的花朵被吞食

    【样例输入】

    6

    3 1

    2 5

    2 3

    3 2

    4 1

    1 6

    【样例输出】

    86

    【样例解释】

     约翰用6,2,3,4,1,5的顺序来运送他的奶牛

    /*
        看到题目比较容易想到要贪心
        对于每个对象有两个元素,一个是时间,一个是消费 
        由题意可知,我们希望先拿走时间短而消费多的 
        为了同时满足两个元素,我们把它合并成一个
        按照消费/时间从大到小排序,然后计算出来,居然就A了 
        (老师说贪心考验人的勇气) 
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n;
    long long ans,sum;
    struct node{
        long long d,t;
        double dt;
    }a[100010];
    int cmp(node x,node y){
        if(x.dt!=y.dt)return x.dt>y.dt;
        if(x.d!=y.d)return x.d>y.d;
        return x.t<y.t;
    }
    int main(){
        freopen("flower.in","r",stdin);
        freopen("flower.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a[i].t,&a[i].d);
            a[i].dt=double(a[i].d)/double(a[i].t);
            sum+=a[i].d;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++){
            sum-=a[i].d;
            ans+=a[i].t*sum*2;
        }
        printf("%I64d",ans);
    }

    Problem 2 修剪草坪(mowlawn.cpp/c/pas)

    【题目描述】

    在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。现在,
    新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。
    然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。FJ有N
    (1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,
    奶牛i的效率为E_i(0 <= E_i <= 1,000,000,000)。
    靠近的奶牛们很熟悉,因此,如果FJ安排超过K(1<=K<=N)只连续的奶牛,那么,这些奶牛就会罢工
    去开派对:)。因此,现在FJ需要你的帮助,计算FJ可以得到的最大效率,并且该方案中
    没有连续的超过K只奶牛。

    【输入格式】
    * 第一行:空格隔开的两个整数N和K
    * 第二到N+1行:第i+1行有一个整数E_i

    【输出格式】
    * 第一行:一个值,表示FJ可以得到的最大的效率值。

    【样例输入】

    5 2

    1

    2

    3

    4

    5

    输入解释:

    FJ有5只奶牛,他们的效率为1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是

    他不能选取超过2只连续的奶牛

    【样例输出】

    12

    FJ可以选择出了第三只以外的其他奶牛,总的效率为1+2+4+5=12。

    #include<iostream>
    #include<cstdio>
    using namespace std; 
    int n,m,a[100010];
    long long ans;
    void dfs(int pos,long long sum,int w){
        if(pos==n+1){
            ans=max(ans,sum);
            return;
        }
        if(w==m){
            dfs(pos+1,sum,0);//下一个牛不能选 
        }
        else{
            dfs(pos+1,sum+a[pos],w+1);//下一个牛选 
            dfs(pos+1,sum,0);//下一个牛不选 
        }
    }
    int main(){
        freopen("mowlawn.in","r",stdin);
        freopen("mowlawn.out","w",stdout);
        //freopen("Cola.txt","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        dfs(1,0,0);
        printf("%d",ans);
        fclose(stdin);
        fclose(stdout); 
        return 0;
    }
    30 分搜索
    /*
        考虑动归,在第i点时,在i-k到i中肯定有一个点j不能选择,即:j为断点。 
        所以f[i]=max(f[i],f[j-1]+a[j+1]+a[j+2]……a[i])(i-k<=j<=i) 
        所以维护前缀和,然后方程就变成了 
        f[i]=max(f[i],f[j-1]+sum[i]-sum[j]) (i-k<=j<=i) 
        变形一下变成:f[i]=max(f[i],f[j-1]-sum[j])+sum[i] (i-k<=j<=i) 
        发现max里面的值只与j有关,所以可以用单调队列优化转移。
    */
    #include<iostream>
    #include<cstdio>
    using namespace std;
    long long n,m,a[100010],sum[100010],f[100010];
    long long d[100010];
    int q[100010],head=0,tail=1;
    long long que(int i){//让返回值尽量的大,队列单调减,使首元素恒最大 
        d[i]=f[i-1]-sum[i];
        while(head<=tail&&d[q[tail]]<d[i])tail--;
        q[++tail]=i;
        while(head<=tail&&q[head]<i-m)head++;
        return d[q[head]];
    }
    int main(){
        freopen("mowlawn.in","r",stdin);
        freopen("mowlawn.out","w",stdout);
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
        for(int i=1;i<=n;i++)f[i]=que(i)+sum[i];
        printf("%I64d",f[n]);
    }
    100分 单调队列优化dp

    Problem 3 虫洞(wormhole.cpp/c/pas)

    【题目描述】

    John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞(有向边)。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。

    【输入格式】

    * Line 1: 一个整数 F, 表示农场个数。

    * Line 1 of each farm: 三个整数 N, M, W。

    * Lines 2..M+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条用时T秒的小路。

    * Lines M+2..M+W+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条可以使John到达T秒前的虫洞。

    【输出格式】

    * Lines 1..F: 如果John能在这个农场实现他的目标,输出"YES",否则输出"NO"。

    【样例输入】

    2

    3 3 1

    1 2 2

    1 3 4

    2 3 1

    3 1 3

    3 2 1

    1 2 3

    2 3 4

    3 1 8

    【样例输出】

    NO

    YES

    /*
        spfa判负环
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    int t,n,m,w,head[25100*4],num,dis[5100],step[5100];
    struct node{
        int to,pre,v;
    }e[25100*4];
    bool vis[5100];
    void Insert(int from,int to,int v){
        e[++num].to=to;
        e[num].v=v;
        e[num].pre=head[from];
        head[from]=num;
    }
    int spfa(){
        queue<int>q;
        q.push(1);step[1]++;
        dis[1]=0;
        vis[1]=1;
        while(!q.empty()){
            int point=q.front();q.pop();vis[point]=0;
            for(int i=head[point];i;i=e[i].pre){
                int to=e[i].to;
                if(dis[to]>dis[point]+e[i].v){
                    dis[to]=dis[point]+e[i].v;
                    if(!vis[to]){
                        q.push(to);
                        step[to]++;
                        vis[to]=1;
                        if(step[to]>n)return 1;
                    }
                }
            }
        }
        return 0;
    }
    int main(){
        freopen("wormhole.in","r",stdin);
        freopen("wormhole.out","w",stdout);
        scanf("%d",&t);
        while(t--){
            memset(e,0,sizeof(e));
            memset(head,0,sizeof(head));
            memset(step,0,sizeof(step));
            memset(dis,127/3,sizeof(dis));
            memset(vis,0,sizeof(vis));
            scanf("%d%d%d",&n,&m,&w);
            int x,y,z;
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&x,&y,&z);
                Insert(x,y,z);
                Insert(y,x,z);
            }
            for(int i=1;i<=w;i++){
                scanf("%d%d%d",&x,&y,&z);
                Insert(x,y,-z);
            }
            if(spfa())printf("YES
    ");
            else printf("NO
    ");
        }
    }

    Problem 4 麻将(data.cpp/c/pas)

    【题目描述】

    众所周知,麻将是我们国家的国粹。这段时间,小D也迷上了麻将这个老少皆宜的游戏。
    D觉得这些不同规则的麻将太麻烦了,所以他集合了很多种麻将规则创造出了一套D麻将。下面是D麻将的几个特点:
    D麻将中有三种花色,万(w)索(s)筒(t),每个花色下有9张牌,每张牌有4个。
    D麻将中没有杠牌,只有顺子和刻子。顺子的含义是相同花色的三张连在一起的牌型(比如说2w3w4w);刻子的含义是三张花色和数字都相同的牌型(比如说2s2s2s)。
    D麻将的胡牌的时候手上往往有14张牌,14张牌凑成了四个顺子或刻子和两张一样的牌做雀头就可以胡牌了。
    D麻将胡牌的时候有很多种不同的牌型,不同的牌型会有不一样的番数。你的一种牌型可能满足了多个加番牌型,满足多个的情况下就把所有满足的牌型的番数全部加起来计算。
    D麻将中有如下牌型可以加番:
    平和(一番):4个顺子组成;
    一杯口(一番):同花色同数值的顺子两组;
    混全带幺九(一番):全部的顺子,刻子中都含有数字1或9;
    三色同顺(一番):三种花色同数值的顺子各一组;
    一气贯通(两番):由同花色一至九组成顺子;
    对对和(两番):四组刻子;

    断幺九(两番):胡牌的时候手上只有2-8的数字组成的牌型;

    一色三顺(三番):同花色同数值顺子三组;
    两杯口(三番):由两组不同的一杯口组成;
    三色同刻(四番):三种花色同数值的刻子各一组;

    清老头(五番):全部由1或9的刻子和雀头组成;
    清一色(七番):全部由同一种花色的顺子,刻子及雀头组成;
    比如说一个牌型为1s2s3s4s5s6s7s8s9s1s2s3s9s9s的牌,它满足了平和、一杯口、一气贯通、清一色四个牌型,所以它的番数是10番。
    D希望为D麻将做一个程序来帮忙判断这个牌型的番数是多少。

    【输入格式】

    输入第一行一个测试组数T。
    接下来T行每行一个字符串s,表示需要判断番数的牌型。length(s)=28

    【输出格式】

    输出有T行每行一个整数,表示判断牌型的番数为多少。

    【样例输入】

    1
    1s2s3s4s5s6s7s8s9s1s2s3s9s9s

    【样例输出】

    10

  • 相关阅读:
    Python 25个关键技术点(附代码)
    win10 LTSC 2019 激活
    【转】我都30岁了,零基础想转行去学编程,靠谱吗?
    查看SELinux状态及关闭SELinux
    Linux下使用route设置路由
    windows下使用route添加路由
    linux中core dump开启使用教程
    如何写好技术文档——来自Google十多年的文档经验
    TCP往返传输时间(RTT)的估计
    【Windows11来了】使用VMware16 pro虚拟机安装WIN11抢先体验
  • 原文地址:https://www.cnblogs.com/thmyl/p/7201825.html
Copyright © 2011-2022 走看看