NOIP 2005 解题报告(过河)

2.过河(river.pas/c/cpp)
【问题描述】
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
【输入文件】
输入文件river.in的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
【输出文件】
输出文件river.out只包括一个整数,表示青蛙过河最少需要踩到的石子数。
【数据规模】
对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。
【解题报告】
最简单的DP方式不难想到,但是L的长度如果是10的9此方的话,直接DP看定要完蛋。
但是可以发现,即使L再大,石子的个数也在100以内,所以说可以采用路径压缩。
至于怎么压的话,可以100,100地压,也可以根据t的值来压(其实不用压的太紧)。

代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

int L,k;  
int S,T,M,i,j,min;  
long stone[102],b[10005];  
int Num[10005]; 
int X[120];

int main()  
{  
    freopen("river.in","r",stdin);
    freopen("river.out","w",stdout);
    memset(Num,-1,sizeof(Num));  
    memset(b,0,sizeof(b));  
    scanf("%d",&L);  
    scanf("%d%d%d",&S,&T,&M);
    for (i=0;i<M;i++)  
    scanf("%d",&stone[i]);
    if(S==T)
    {  
        min = 0;  
        for (i=0;i<M;i++)  
        {  
            if (stone[i] % S == 0)  
                min++;  
        }  
        printf("%d",min);  
        return 0;
    }  
    for (i=0;i<M-1;i++)  
    {  
        for (j=0;j<M-i-1;j++)  
            if(stone[j]>stone[j+1])  
            {  
                int temp;
                temp = stone[j];  
                stone[j]=stone[j+1];  
                stone[j+1]=temp;  
            }  
    }  
    stone[M]=L; 

    for(i=0;i<M;i++)  
    X[i]=stone[i+1]-stone[i]; 

    if (stone[0]>90)  
    {  
        k = stone[0]-90;  
        for(i=0;i<=M;i++)  
        stone[i] -= k;  
    }  
    for (i=1;i<=M;i++)  
    {  
        if (stone[i]-stone[i-1]>90)  
        {  
            k = stone[i]-stone[i-1]-90;  
            for(j=i;j<=M;j++)  
            stone[j] -= k;  
        }  
    }   

    for (i=0;i<M;i++)  
    b[stone[i]]=1;  
    Num[0]=0;  
    for (i=S;i<stone[M]+T;i++)  
    {  
        min = 101;  
        for (j=i-T;j<=i-S;j++)  
        {  
            if (Num[j]<min && Num[j]!=-1&&j>=0)  
            min=Num[j];  
        }  
        if (min!=101)  
        Num[i]=min+b[i];  
    }  
    min=101;  
    for (i=stone[M];i<stone[M]+T;i++)  
    {  
        if (Num[i]<min&&Num[i]!= -1)  min = Num[i];  
    }  
    printf("%d",min);    
    return 0;  
}  

3.篝火晚会(fire.pas/c/cpp)
【问题描述】 佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。 佳佳可向同学们下达命令,每一个命令的形式如下: (b1, b2,… bm-1, bm) 这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是(b1, b2,… bm-1, bm)的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。 执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?
【输入文件】 输入文件fire.in的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。
【输出文件】 输出文件fire.out包括一行,这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。
【解题报告】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值