2013.7.14 acm_schooltraining 解题报告

话说没有完成任务就不要悠闲地去睡觉啊~

Problem A: 神奇的fans

Time Limit: 1 Sec   Memory Limit: 128 MB Submit: 84   Solved: 30[ Submit][ Status][ Web Board]

Description

传说fans是一个数学天才。在他五岁那年,从一堆数字卡片中选出了4张 卡片:5,7,6,8。这4个数字有什么神秘之处呢?如果把这4张卡片自左往右的排成:5,6,7,8。你就会发现:原来这4个数字构成了等差数列!当年 fans选出了n组卡片,据说都能够构成等差数列。但是事实真的是这样吗?fans真的有这么神奇吗? n组数据就是fans选出的n组卡片,请你判断每一组卡片是否能构成等差数列.

Input

第一个数为数据的组数n,表示后面有n行,每行中的第一个数为该组数据的元素个数m(1≤m≤100),其后是m个正整数(不会超出int的表示范围)。

Output

如果能够构成等差数列,输出“yes”,否则输出“no”。

Sample Input

24 5 7 6 88 1 7 3 2 8 12 78 3

Sample Output

yesno

HINT

此题属简单题(话说自己现在就这种水平,路漫漫其修远兮,吾将上下而求索呀~);
关键点:排序 等差中项(由于数列不是很大因而逐项比较,注意循环条件)
way:
#include 
#include
using namespace std;
int a[100];
int cmp(int a,int b)
{
    return a>b;
}
int main()
{
    int n,m,flag;
    cin>>n;
    while(n--){
         cin>>m;
         flag=1;
         for(int i=0;i>a[i];
         sort(a,a+m,cmp);        //从小到大排个序
         for(int i=0;i

Problem B: 伊甸园日历游戏

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 18   Solved: 5
[ Submit][ Status][ Web Board]

Description

      Adam和Eve玩一个游戏,他们先从1900.1.1到2001.11.4这个日期之间随意抽取一个日期出来。然后他们轮流对这个日期进行操作:       1  :  把日期的天数加1,例如1900.1.1变到1900.1.2       2  :  把月份加1,例如:1900.1.1变到1900.2.1       其中如果天数超过应有天数则日期变更到下个月的第1天。月份超过12则变到下一年的1月。而且进行操作二的时候,如果有这样的日期:1900.1.31,则变成了1900.2.31,这样的操作是非法的,我们不允许这样做。而且所有的操作均要考虑历法和闰年的规定。       谁先将日期变到2001.11.4谁就赢了。       每次游戏都是Adam先操作,问他有没有必胜策略?

Input

    一个测试点。多组数据。     第一行为数据组数。     接下来一行X  Y  Z表示X年Y月Z日

Output

    输出“YES”or“NO”表示亚当是否有必胜策略。 

Sample Input

3
2001 11 3
2001 11 2
2001 10 3

Sample Output

YES
NO
NO

HINT

 建议先把所有情况都算出来^_^

分析:初读题目感觉挺麻烦的一道题,一看提示就吓得不敢做了。但是同学找到的规律却是简单的——y+z(y为月,z为日)如果为偶数则必胜,反之则输出“N”,但是有两组特殊情况是9.30和11.30也是可以的。虽说规律简单,但推起来也不简单,还是要自己在推推。
另外,本题有一般做法。
Q:年份是多余的条件?
//vijos-p1004
#include 
using namespace std;

int main()
{
    int t,x,y,z;
    cin>>t;
    while(t--){
        cin>>x>>y>>z;
        if((y==9&&z==30)||(y==11&&z==30))
            cout<<"YES"<

Problem C:  核电站问题

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 76   Solved: 13
[ Submit][ Status][ Web Board]

Description

        一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。         现在,请你计算:对于给定的N和M,求不发生爆炸的放置核物质的方案总数。

Input

输入文件只有一行,两个正整数N,M。

Output

输出文件只有一个正整数,表示方案总数。

Sample Input

4 3

Sample Output

13

HINT

全部数据n< =50,m< =5

分析:最初是先将全部情况都列出(2^n——每个坑都可以填,或者不填),然后减去会爆炸的情况(连续的x个炸弹(n>=x>=m)的排列情况(i-m-1, i-m-2,……,1)),然后WA之后突然意识到没有考虑当x>m时x是可以拆成x=m+y的,所以还是用递推:f[i]=2*f[i-1]-f[i-m-1]; f[i]表示有i个坑的时候的情况,之所以减去f[i-m-1]是因为在f[i-1]的情况*2的情况下有一组总的坑为n,但最后有连续的m个炸弹,所以要减去此种请况中多算的i-m-1种情况。对于f[4]和f[5]个人的理解是方便之后的递推(拙见)
Q: f[4]=1的具体含义?

way:
#include 
#include 
#include 
using namespace std;
long long  f[60];


int main()
{
    int n,m;
    while(cin>>n>>m){
        f[4]=f[5]=1;
        for(int i=6;i

Problem D: Function(Function(F...

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 58   Solved: 13
[ Submit][ Status][ Web Board]

Description

对于一个递归函数w(a,b,c) 如果a< =0  or  b< =0  or  c< =0就返回值1. 如果a> 20  or  b> 20  or  c> 20就返回w(20,20,20) 如果a< b并且b< c  就返回w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c) 其它别的情况就返回w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1) 这是个简单的递归函数,但实现起来可能会有些问题。当a,b,c均为15时,调用的次数将非常的多。你要想个办法才行.

Input

会有若干行. 并以-1,-1,-1结束.

Output

输出若干行

Sample Input

1 1 1
2 2 2
-1 -1 -1

Sample Output

w(1, 1, 1) = 2
w(2, 2, 2) = 4
分析:看似是个简单的递归,但是有提示会超时,所以可以在递归的时候用备忘录记录过程值(所谓过程值也是相对说法,就是把计算过的结果保留下来,方便之后使用),一开始不知道怎么使用备忘录,请教了同学之后明白了——先判断边界条件,然后再看该组数据的结果是否已经记录过(有标记来看),是的话直接返回结果,不然进行计算,然后保存结果;还有另一种方法是递推法
way1:
#include 
#include 
#include
using namespace std;
#define N -999999
int vis[25][25][25];

long long w(int a,int b,int c)
{
    if(a<=0||b<=0||c<=0)
       return 1;
    else if(a>20||b>20||c>20)
       return w(20,20,20);
    if(vis[a][b][c]!=N)
        return vis[a][b][c];
    else if(a>a>>b>>c){
        if(a==-1&&b==-1&&c==-1)
           break;
        for(int i=0;i<=20;i++)
        for(int j=0;j<=20;j++)
        for(int k=0;k<=20;k++)
            vis[i][j][k]=N;
        printf("w(%d, %d, %d) = %d\n",a,b,c,w(a,b,c));
    }
    return 0;
}

way2:
#include 
#include 
#include
using namespace std;
#define N -999999
int f[25][25][25];


int main()
{
    int a,b,c;
    for(int i=0;i<=20;i++){
      for(int j=0;j<=20;j++){
        for(int k=0;k<=20;k++){
             if((i==0)||(j==0)||(k==0))  f[i][j][k]=1;
             else if((i>a>>b>>c){
         if(a==-1&&b==-1&&c==-1)
             break;
         if((a<=0)||(b<=0)||(c<=0))
             printf("w(%d, %d, %d) = 1\n",a,b,c);
         else if((a>20)||(b>20)||(c>20) )
             printf("w(%d, %d, %d) = %d\n",a,b,c,f[20][20][20]);
         else
             printf("w(%d, %d, %d) = %d\n",a,b,c,f[a][b][c]);
    }
    return 0;
}

Problem E: VIJOS-P1292

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 9   Solved: 8
[ Submit][ Status][ Web Board]

Description

一个铁路线上有n(2< =n< =10000)个火车站,每个火车站到该线路的首发火车站距离都是已知的。任意两站之间的票价如下表所示:
 站之间的距离    X             票价
 0< X< =L1                        C1
 L1< X< =L2                      C2 
L2< X< =L3                       C3 
其中L1,L2,L3,C1,C2,C3都是已知的正整数,且(1  < =  L1  <   L2  <   L3  < =  10^9,  1  < =  C1  <   C2  <   C3  < =  10^9)。显然若两站之间的距离大于L3,那么从一站到另一站至少要买两张票。注意:每一张票在使用时只能从一站开始到另一站结束。 现在需要你对于给定的线路,求出从该线路上的站A到站B的最少票价。你能做到吗?

Input

输入文件的第一行为6个整数,  L1,  L2,  L3,  C1,  C2,  C3  (1  < =  L1  <   L2  <   L3  < =  10^9,  1  < =  C1  <   C2  <   C3  < =  10^9)  ,这些整数由空格隔开.第二行为火车站的数量N  (2  < =  N  < =  10000).第三行为两个不同的整数A、B,由空格隔开。接下来的  N-1  行包含从第一站到其他站之间的距离.这些距离按照增长的顺序被设置为不同的正整数。相邻两站之间的距离不超过L3.  两个给定火车站之间行程花费的最小值不超过10^9,而且任意两站之间距离不超过  10^9。

Output

输出文件中只有一个数字,表示从A到B要花费的最小值. 

Sample Input

3 6 8 20 30 40 
2 6 
3 7 8 13 15 23

Sample Output

70

HINT

这道题根本就没往复杂处想,拿来就直接做了,自己的思路是先求出出发站与到达站之间的距离,然后按照他所在的范围一次减去L1、2、3,同时加上相应的票价。同学用的是DP,暂时没弄明白,再去讨教讨教。话说因为之前能过是因为只有一组数据,惭愧!
大神代码:

Problem F: 搭建双塔

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 15   Solved: 8
[ Submit][ Status][ Web Board]

Description

        2001年9月11日,一场突发的灾难将纽约世界贸易中心大厦夷为平地,Mr.  F曾亲眼目睹了这次灾难。为了纪念“9?11”事件,Mr.  F决定自己用水晶来搭建一座双塔。         Mr.  F有N块水晶,每块水晶有一个高度,他想用这N块水晶搭建两座有同样高度的塔,使他们成为一座双塔,Mr.  F可以从这N块水晶中任取M(1≤M≤N)块来搭建。但是他不知道能否使两座塔有同样的高度,也不知道如果能搭建成一座双塔,这座双塔的最大高度是多少。所以他来请你帮忙。         给定水晶的数量N(1≤N≤100)和每块水晶的高度Hi(N块水晶高度的总和不超过2000),你的任务是判断Mr.  F能否用这些水晶搭建成一座双塔(两座塔有同样的高度),如果能,则输出所能搭建的双塔的最大高度,否则输出“Impossible”。

Input

        输入的第一行为一个数N,表示水晶的数量。第二行为N个数,第i个数表示第i个水晶的高度。

Output

        输出仅包含一行,如果能搭成一座双塔,则输出双塔的最大高度,否则输出一个字符串“Impossible”。

Sample Input

5
1 3 4 5 2

Sample Output

7

HINT

分析:DP,dp[i][j](两座塔的高度i,j)如果为1则存在,若为0则不存在;最后找dp[i][j]最大的,若没有则输出 “Impossible”;
way:
#include 
#include
using namespace std;
int h[105];
int dp[2010][2010];


int main()
{
   int n,sum=0,flag;
   while(cin>>n){
       flag=0;
       for(int i=0;i>h[i];
       memset(dp,0,sizeof(dp));
       dp[0][0]=1;
       for(int i=0;i=0;j--)
       for(int k=1001;k>=0;k--){
            if(dp[j][k]){
               if(j+h[i]<=1001)
                  dp[j+h[i]][k]=1;
               if(k+h[i]<=1001)
                  dp[j][k+h[i]]=1;
            }
       }
       for(int i=1001;i>0;i--)
           if(dp[i][i]){
              cout<

Problem G: 过河

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 10   Solved: 2
[ Submit][ Status][ Web Board]

Description

        在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。         题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。         对于30%的数据,L  < =  10000;         对于全部的数据,L  < =  10^9。

Input

        输入的第一行有一个正整数L(1  < =  L  < =  10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1  < =  S  < =  T  < =  10,1  < =  M  < =  100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

Output

        输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。

Sample Input

10
2 3 5
2 3 5 6 7

Sample Output

2
分析:DP之外,因为桥长度很长,但是石子却很少,这就要进行状态压缩。这里用S,T之间的lcm来进行压缩,(因为LCM是S到M之间任意数的倍数),状态方程为:
dp[i]=dp[i-j]+flag[i](s<=j<=t,flag[i]为i点有无石头)
way:
//VIJOS-P1002
#include 
#include 
#define N 3200000
#define MAX 999
using namespace std;
int dp[N],flag[N];
int s,t,m,a[110];
int GCD(int a,int b)
{
    return b==0?a:GCD(b,a%b);
}

int main()
{
    int l,count;
    cin>>l>>s>>t>>m;
    for(int i=1;i<=m;i++)
        cin>>a[i];
    int lcm=1;
    for(int i=2;i<=10;i++)
        lcm=lcm*i/GCD(lcm,i);
    sort(a+1,a+m+1);
    a[m+1]=l;            //跳出
    for(int i=1;i<=m;i++)
        if(a[i+1]-a[i]>lcm)
            a[i+1]=a[i]+(a[i+1]-a[i])%lcm; //压缩
    l=a[m+1];       //更新l
    for(int i=1;i<=m;i++)     flag[a[i]]=1;
    for(int i=1;i<=l+t;i++)   dp[i]=MAX;    //可跳过l+t
    for(int i=1;i<=l+t;i++)
        for(int j=s;j<=t;j++)
            if(i>=j&&dp[i]>dp[i-j]+flag[i])
              dp[i]=dp[i-j]+flag[i];
    count=MAX;
    for(int i=l;i<=l+t;i++)
        if(count>dp[i])
            count=dp[i];
    cout<



Problem H: 过河卒

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 69   Solved: 14
[ Submit][ Status][ Web Board]

Description

如图,A 点有一个过河卒,需要走到目标 B 点。卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。例如上图 C 点上的马可以控制 9 个点(图中的P1,P2 … P8 和 C)。卒不能通过对方马的控制点。

棋盘用坐标表示,A 点(0,0)、B 点(n,m)(n,m 为不超过 20 的整数,并由键盘输入),同样马的位置坐标是需要给出的(约定: C<>A,同时C<>B)。现在要求你计算出卒从 A 点能够到达 B 点的路径的条数。

Input

键盘输入
B点的坐标(n,m)以及对方马的坐标(X,Y){不用判错}

Output

屏幕输出
一个整数(路径的条数)。

Sample Input

6 6 3 2

Sample Output

17

HINT

话说这道题也忒wired了,交了n次都不过,最好还是仰仗大神了。思路倒是挺清楚的,因为不用判断错所以就简化了不少
动态方程为dp[i][j]=dp[i-1][j]+dp[i][j-1](从0,0点到i,j点的路径),当然要判断是否为马的控制点,如果是马的控制点应该为0。

Q: 话说当i=0,j=0是上面的方程会出现负数,但是将整个dp矩阵输出之后观察是对的,需要再问问他人
#include 
#include 
#include 
using  namespace std;
long long  dp[30][30],vis[30][30];
int n,m,x,y;

int main ()
{
     while(scanf("%d%d%d%d",&n,&m,&x,&y)!=EOF)
     {
         memset(dp,0,sizeof(dp));
         dp[0][0]=1;
         memset(vis,0,sizeof(vis));
         vis[0][0]=1;
         vis[x][y]=1;     vis[x+2][y+1]=1; vis[x+1][y+2]=1;  //马控制点
         vis[x-2][y-1]=1; vis[x-1][y-2]=1; vis[x+1][y-2]=1;
         vis[x+2][y-1]=1; vis[x-1][y+2]=1; vis[x-2][y+1]=1;
         for(int i=0; i<=n; ++i)
         {
             for(int j=0; j<=m; ++j)
             {
                 if(!vis[i][j])                         //如果不是马控制点
                 dp[i][j]=(dp[i-1][j]+dp[i][j-1]);
             }
         }
         printf("%lld\n",dp[n][m]);
     }
 }



你可能感兴趣的:(acm)