2014-5-24 NOIP模拟赛

时间:2022-12-17 11:22:18

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。

2014-5-24 NOIP模拟赛2014-5-24 NOIP模拟赛
#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 分搜索
2014-5-24 NOIP模拟赛2014-5-24 NOIP模拟赛
/*
    考虑动归,在第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\n");
        else printf("NO\n");
    }
}

 

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