2010年9月22号校内ACM省赛前模拟赛教师总结外加部分结题报告

模拟练习1

主要问题

1、 开场1小时比较混乱,基本没有战术,主将的作用未体现。未及时发现典型题、水题。

2、 两只08级队对模板的使用不熟练、不敏感。

3、 有些队员有劲使不上。

反思重点

1、 策略?读题策略?见题就做,还是通读所有题目再做?队长或主将如何发挥作用?每个小组应当有自己的具体安排。

整体上,我们应当定位在:

(1)通读题目、交流,找出水题。千万不要赛后发现有道水题没人仔细读过。每个队应有主将或队长保持清醒的大局观。

(2)典型题套用模板以提高效率。

(3)结对编程、减少出错。主将拿点时间听副将说自己的看法,否则很可能副将工作无效。

注意:

(1)最终比赛结果主要取决于AC的题目数量。

(2)省赛现场赛可能没有足够多足够强的队,很快的AC让我们准确发现所有的水题。

2、 遇到水题中的偏题、甚至错题,如何调整?

选择1:给队友讲题,以验明思路;

选择2:换个题做;省赛水题应有不少,调整到别的题目,可能别有洞天。

选择3:起来短时活动透气喝水,或殴打队友中较壮者。

是否题出错了?应是在最后怀疑的,除非对题目特别有掌控感。更常见是特例数据,输出格式,或者是低级错误。

3、 如何在典型题中使用模板?

要看准备的情况了。但省赛应该有用武之地。例如本次模拟赛的DP问题,应利用模板。

4、 赛场会有各种干扰信息。

例如别的队的进度,来往纪律人员,来往打印的选手。

听到别人讨论的只言片语也是会有的事情。例如,“1005是这次比赛最简单的题”。母函数或DP对某些队来说可能并没有那么简单。例如,“第7题和第8题可能很简单”,第7题或许是最难的一题。

其参考意义是相对的。

当然,自己的话也会被别组听到。

题目清单及解题报告

HDOJ原题号、题名

来源

AC(截至9.21)

比赛题号

1597 find the nth digit

HDU 2007-Spring Programming Contest - Warm Up 1

(863/3140)27.48%

1

3270 The Diophantine Equation

HDOJ Monthly Contest – 2010.01.02

(178/667)26.69%

2

3272Mission Impossible

HDOJ Monthly Contest – 2010.01.02

(53/137)38.69%

3

3275Light

HDOJ Monthly Contest – 2010.01.02

(109/366)29.78%

4

3274City Planning

HDOJ Monthly Contest – 2010.01.02

(106/251)42.23%

6

3271SNIBB

HDOJ Monthly Contest – 2010.01.02

(37/214)17.29%

7

1398 Square Coins

Asia 1999, Kyoto (Japan)

(1997/2963)67.40%

5

1027Ignatius and the Princess II

 

(728/1259)57.82%

8

2206IP的计算

 

(540/2958)18.26%

10

2084数塔

2006/1/15 ACM程序设计期末考试

(3131/5285)59.24%

9

2084数塔

这是动态规划的一道基础题,有形如上图所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的值最大。从顶点出发时到底向左走还是向右走应取决于是从左走能取到最大值还是从右走能取到最大值,只要左右两道路径上的最大值求出来了才能作出决策。同样,下一层的走向又要取决于再下一层上的最大值是否已经求出才能决策。这样一层一层推下去,直到倒数第二层时就非常明了。如数字2,只要选择它下面较大值的结点19前进就可以了。所以实际求解时,可从底层开始,层层递进,最后得到最大值。结论:自顶向下的分析,自底向上的计算。这里我们也可以用两种方法来求解这道题,一种是自顶向下,采用递归的方式就可以了,另一种是自底向上,只要普通的循环就行。

递归的方法:从tower[i][j]开始,求经过的结点的数字和最大,假如我们知道从它的左节点tower[i+1][j]或右节点tower[i+1][j+1]开始的数字和最大值,只要比较一下左边和右边哪个值更大,最后再加上tower[i][j]就可以了,那要知道左边的节点的最大值就再往下找它的左节点和右节点,这就是递归的思想。终止条件是当i等于塔的高度的时候,也就是走到最底下的节点,且这个节点没有孩子,就直接比较左右节点并返回。这里的递归出现很多的重复计算,比如上图的6节点是12的右节点又是15的左节点,所以我们要做一定的优化,用一个数组f[N][N]来记录已经计算过的节点的值。

#include <iostream>

using namespace std;const int N=110;int tower[N][N];int f[N][N];int height;void Init(){    scanf("%d",&height);    for(int i=1;i<=height;i++)        for(int j=1;j<=i;j++)            scanf("%d",&tower[i][j]);    memset(f,-1,sizeof(f));}int DP(int i,int j){    int ans;    if(f[i][j]>=0)      return f[i][j];    if(i==height)       ans=tower[i][j];    else    {      if(DP(i+1,j)>DP(i+1,j+1))                  ans=tower[i][j]+DP(i+1,j);      else         ans=tower[i][j]+DP(i+1,j+1);     }    f[i][j]=ans;    return f[i][j];}int main(){   int Case;   scanf("%d",&Case);   while(Case-->0)   {       Init();       printf("%d/n",DP(1,1));         }   return 0;}   

 

自底向上的方法: 

#include <stdio.h> #include <memory.h> const int N=110;int tower[N][N];int tmax[N][N];int height;void Init(){    scanf("%d",&height);    for(int i=1;i<=height;i++)        for(int j=1;j<=i;j++)            scanf("%d",&tower[i][j]);}int max(int a,int b){    return a>b?a:b;}int main(){    int Case;    scanf("%d",&Case);    while(Case--)    {        Init();        memset(tmax,0,110*110*sizeof(int));        for(int i=height;i>0;i--)           for(int j=1;j<=i;j++)            {               if(i==height)                  tmax[i][j]=tower[i][j];               else                   tmax[i][j]=tower[i][j]+max(tmax[i+1][j],tmax[i+1][j+1]);          }         printf("%d/n",tmax[1][1]);        }    return 0;}

 

1398 Square Coins

/*
母函数DP
hdu1398
ymc 2008/09/25
题目大意:
由面值为1,4,9,16..289的硬币,构成总值为
n
,总共有多少种方法?
解题思路:
f(x)=(1+x+x^2+...)(1+x^4+x^8+...)..(1+x^289+...)
其中x^n次方的系数就是构成总值为n的方法数。
具体参考 生成函数,也叫母函数。
*/
#include <iostream>
using namespace std;
#define lmax 301
int c1[lmax],c2[lmax];
int main()
{
    int i,j,k,n;
    while(cin>>n && n)
    {
      for(i=0;i<=n;i++)
         c1[i]=1,c2[i]=0;
      for(i=2;i<=17;i++)              //
括号数
         {
             for(j=0;j<=n;j++)        //
第一项 元素个数
                   for(k=0;j+k<=n;k+=i*i)
                       c2[j+k]+=c1[j]; //
             for(j=0;j<=n;j++)
                c1[j]=c2[j],c2[j]=0;
         }
         cout<<c1[n]<<endl;
    }
    return 0;
}

 

2206 IP的计算

麻烦的模拟题~~~~

说下这题要注意的几点吧。

1.不能有除了数字和’.'以外的其他字符,不光是判断空格。

2. .’ 的数目必须是三个

3.2个’.'之间的数字大小是0~255

4.2个’.'之间的数字的个数要小于等于3个,且不能是0个。

5.根据我自己的思路,在判断三个’.'时,对第四个数要额外考虑。

    考虑大小,个数等

   比如:100.110.110.   |   100.110.110.1000 |   100.110.110.333

   这些情况都是NO,都得考虑。代码有点杂。大家互相学习吧。

这里多层循环退出麻烦,我第一次用了goto~~~呵呵。

#include <iostream>

#include <cmath>

#include <algorithm>

#include <string>

using namespace std;

 

char ip[101];

int nCases;

int main()

{

T:while(gets(ip))

    {

        int len = strlen(ip);

        for(int i=0; i<len; ++i)   // 出现除了数字和'.'外其他数,不符

        {

            if((ip[i]>'9' || ip[i]<'0') && ip[i] != '.')

            {

                printf("NO/n");

                goto T;

            }

        }

        int cnt = 0;

        int num = 0;

        int num_cnt = 0;

        for(int i=0; i < len; ++i)

        {

            if(ip[len-1] == '.')   // 最后一位是'.'

            {

                printf("NO/n");

                goto T;

            }

            if(ip[i] == '.')

            {

                if((num < 0 || num > 255) || num_cnt==0 || num_cnt>3)   // 数字大于255或者位数大于3或等于0

                {

                    printf("NO/n");

                    goto T;

                }

                num = 0;

                num_cnt = 0;

                cnt++;

                continue;

            }

            num *= 10;

            num += ip[i]-'0';

            num_cnt++;

            if(i == len-1)

            {

                if((num < 0 || num > 255) || num_cnt==0 || num_cnt>3)   // 数字大于255或者位数大于3或等于0

                {

                    printf("NO/n");

                    goto T;

                }

            }

        }

        if(cnt == 3)

            printf("YES/n");

        else

            printf("NO/n");

    }

    return 0;

}

1027 Ignatius and the Princess II

数学题(或用STL 

以下代码使用STL

49 #include<iostream>
50 #include<vector>
51 #include<algorithm>
52 using namespace std;
53 int a[10000],coun,n,m,i;
54 int main()
55 {
56     while(cin>>n>>m)
57     {
58         for(i=0;i<n;i++)
59         {
60             a[i] = i+1;
61         }
62         vector<int> iv(a,a+n);
63         coun = 1;
64         //next_permutation : Change the order of the sequence to the next lexicograhic permutation.
65
66         while(next_permutation(iv.begin(),iv.end()))
67         {
68             coun++;
69             if(coun==m)
70                 break;
71         }
72         printf("%d",iv[0]);
73         for(i=1;i<n;i++)
74             printf(" %d",iv[i]);
75         printf("/n");
76     }
77     return 0;
78 }

 

 
3270 The Diophantine Equation

Use the Extended Euclidean algorithm to find the integers x0 and y0 which make a*x0 + b*y0 = c, for a*(x0 - b) + b*(y0 - a) = c, we can test if there’s a non-negative solution. As the range of data is small, some easier methods will do.

/*
***************************************
HDU 3270 The Diophantine Equation
Tips
暴力
memory 204K
runtime 15MS
language C++
***************************************
*/
#include<iostream>
#include<string>
using namespace std;
int main(){
int a,b,c;
char s[100];
while(gets(s)){
int t=0;
int len=strlen(s);
bool flag=false;
for(int i=0;i<len;i++){
if(s[i]=='x'){
if(t==0) a=1;
else {
a=t;
t=0;
}
}
if(s[i]=='y'){
if(t==0) b=1;
else {
b=t;
t=0;
}
}
if(s[i]>='0' && s[i]<='9') t=t*10+s[i]-'0';
if(s[i]=='-') flag=true;
}
if(!flag) c=t;
else c=-t;
if(c<0){
printf("No./n/n");
}else if(c==0){
printf("Yes./n/n");
}else{
for(int x=0;;x++){
int tmp=c-a*x;
if(tmp==0) printf("Yes./n/n");
else if(tmp<0){
printf("No./n/n");
break;
}else{
if(tmp%b==0){
printf("Yes./n/n");
break;
}
}
}
}
}
return 0;
}

3271 SNIBB

When Q = 1, the complexity of the brute force algorithm is O (| Y-X | * C), C is the complexity of calculating the sum of all digits in Base B.This way you will certainly get TLE. Starting from the specificity of the problem.It is not difficult to think of a recursive method.

Here is a simple sample showing how to use the the recursive method.

Assuming that we need to calculate how many figures (on Base 10) on interval [0,127] satisfy that the sum of all digits of the figure is 3.

We can first deal with interval [121,127], and then add the answer on [0,120].

To get the number of satisfied figures on interval [0,120], We can enumerate the last digit (0,1,2 ... 9), and then for a determined digit, such as enumeration to 2, So we need to consider {2,12,22... 102,112}

Obviously these figures can be simply written as M*10+2, so we only need to calculate the number of figures on interval [0,11] that satisfy the sum of all digits of the figure is m-2.(on Base 10), because when a number k meets this condition , there must be k*10+2 figures that the sum of all digits of the figure is m.

Therefore, the problem is translated into calculating the number of figures on interval [0,11] that satisfy the sum of all digits of the figure is 1. It is clearly that on interval [11, 11] no numbers meet the conditions. Considering interval [0,10] ,we can get the number of figures meeting the condition in O(1) time. We must be careful that the number m can not be less than 0, but can equal to 0.

 Another example is for the interval [0,9971], first transformed into [0,9970], and then enumerate the last digit, note that if the enumeration is 0, then it should recursively interval [0,997], while the others are only recursive interval [0,996]. Then continue with the recursive, solution can be obtained.

However, if only use this method you may also get TLE, Considering a large number of multi-counting, you can use Hash or map to save the solution on interval [0,x’] corresponding to the number m’ to avoid multi-counting.

When Q = 2, we must take advantage of the functions above, and continuously devide the partition into two parts until get a mid, to satisfy Num([L,mid])=k-1, L is the left point of the interval. And from L, starting the enumeration (Note that the Base is very small, <= 64). We should specially take attention to the case when k = 1 or the answer does not exist. There is a small trap that when using the Binary-Search Method, the variable left, right, and mid should use 64-bit integer , otherwise it will overflow in some data.

3272 Mission Impossible

To get the shortest path between A and B, which passes line C, we can draw A’s symmetry point A’ relative to C, then calculate then length of A’C.

Similarly, we can get the shortest path between two points, which passes two axes. Enumerate all possible paths, and get the smallest one, that is the answer.

3274 City Planning

Brute Force and DFS will work at this problem.

For each edge, if without it, the m villages will divide into several parts, it must been chose.

3275 Light

Greedy and Segment Tree will be worked at this problem.

We need change all “0” to “1”, so let’s put our attention on the first lantern of the given string. There is only one way to change states of first lantern which is installing a switch to control the first k lanterns.

Algorithm:

1. If the first lantern is off then install a switch to change the states of first k lanterns.

2. Let the second lantern be the first one and repeat Step 1 until there are less than k lanterns.

3. If all of the remaining lanterns are on then output the answer else output “-1”.

Native solution will get TLE as it’s running in O(n * k).

A new accelerated date structure should be used to storage all the states of lanterns. This date structure should support two operators.

1. Find the states of one lantern.

2. Change all states of an interval.

Segment Tree will be worked.

3276 Star

Binary search and DP will be worked at this problem.

At first, you can binary search the answer, then subtract the value from each number in the list.

Now the question is to find out two segments whose sum is not less than 0.This can been solved by dp.

We can enumerate the middle part of the two segments, then find out the biggest two segments. Since the strategy satisfy the monotonic, the O(n) algorithm will work here.

1597 find the nth digit

方法一

当时做的时候,都是因为太急,把公式想得太简单了,然后,就稀里糊涂的写了个程序,发现结果差太多了;然后重新推倒公式,发现那个求出来的结果是不准确的,然后又写了一个,这次感觉没错了,WA;然后,又仔细看题,发现根本就没有两位数以上的,晕……;改了又交,WA;又瞅了好久,才发现数据的乘积已经超过int,只能改用double,结果还是WA;然后,才发现并不只是最后的结果需要改为double,中间的变量也要改,因为在乘的过程中,早就超过int了;

 

小题目折射出大问题啊……

 

#include<stdio.h>

#include<math.h>

int main()

{

 int i,j,k,t;

 double s,m,n;

 scanf("%d",&k);

 for(i=1;i<=k;i++)

 {

  scanf("%lf",&n);

  m=(sqrt(n*2));

  j=(int)(m);

  m=m-(m-j);

  s=m*(m+1)/2;

  if(s<n)

   m=m+1;

  s=m*(m-1)/2;

  t=(int)(n-s)%9;

  if(t==0)

   t=9;

  printf("%d/n",t);

 }

 return 0;

}

 

方法二:203MS 292K

 

#include <iostream>

using namespace std;

int main()

{

    int k,n,j;

    cin>>k;

    while(k--)

    {

        j=1;

        cin>>n;

        if(n>450015000) //1+2+...+30000

        {

            j=30001;

            n-=450015000;

        }

        while (n>j)

        {

          n-=j;

          j++;

        }

        n=n%9;

        if(n==0) n=9;

        printf("%d/n",n);

    }

    return 1;

}

 

方法三:31MS 552K 二分法 由于数据规模不大 优化的时间不太明显

 

#include <iostream>

using namespace std;

int binarySearch(int a[],int n)

{

    int low=1,high=65536,mid;

 

    while(low<=high)

    {

        mid=(low+high)/2;

        if(a[mid]==n)

            return mid;

        else if(a[mid]<n)

            low=mid+1;

        else

            high=mid-1;

    }

    return low;

}

 

int main()

{

    int i,n,k,a[65555]={0};

    for(i=1;i<65536;i++)

        a[i]=a[i-1]+i;

    a[65536]=INT_MAX;

 

    cin>>k;

    while(k--)

    {

        cin>>n;

        int pos=binarySearch(a,n);

        n=n-a[pos]+pos;

        n%=9;

        if(n==0) n=9;

        cout<<n<<endl;

    }   

    return 1;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值