2013.4.25 四月份月赛解题报告


比赛排行榜:

http://acm.hdu.edu.cn/webcontest/contest_ranklist.php?cid=4674&page=1

密码:

iloveacm


No matter how low your grades are , just don't give up .

All you need to do is holding on . 

It's hard , but it's beautiful !  

And you will appreciate your now perseverance in the soon future .

     

第一题:(poj 1068 Parencoding)(简单模拟题)

       这道题目关键是读懂题意,读懂了后模拟下过程就可以过。没过的再读读题目,水题。

题意:

	S		(((()()())))

	P-sequence	    4 5 6666

	W-sequence	    1 1 1456

S 是一个完全匹配的括号序列,注意是完全匹配。

P 数列表示每个右括号左边的左括号的个数。

W 数列表示每个右括号从与该右括号匹配的左括号到该右括号之间的右括号的个数(包括两端点)。

告诉P数列,求对应的S数列。

解题思路:

     显然W数列的个数与P数列的个数相同(都是对应的右括号的)。所以以右括号划分区间,注意求W数列的区间的括号是完全匹配,所以问题就转化为,最靠近当前右括号的没有匹配的的左括号,之中右括号的个数(也就是区间的长度)。vis[i]表示第i-1个右括号到第i个括号之间已匹配的左括号数。详见代码。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

int p[30],w[30],vis[30];

int main()
{
   int t,n;

   scanf("%d",&t);
   while(t--)
   {
      memset(vis,0,sizeof(vis));

      scanf("%d",&n);
      for(int i=1;i<=n;i++)
         scanf("%d",&p[i]);
      w[1]=1;
      vis[1]=1; //第零个右括号和第1括号之间已经匹配了一个左括号

      for(int i=2;i<=n;i++)
      {
         int j,sum=0;
         for(j=i-1;j>=1;j--) //一直找到最靠近当前右括号的左括号
            if(p[j+1]-p[j]-vis[j+1])  //表示第j个右括号到第j+1个右括号之间除去已经匹配了的左括号以外还有左括号供匹配,找到了最靠近的
               break;
         vis[j+1]++; //把该区间的已匹配的左括号加一
         w[i]=i-j;   //统计右括号的个数(注意是从第i+1到第j个右括号
      }
      printf("%d",w[1]);
      for(int i=2;i<=n;i++)
         printf(" %d",w[i]);  //注意输出格式,不要多添加空格
      putchar('\n');
   }
   return 0;
}





第二题:hdu 4190 ( 简单二分法)

      这题没出来,说明二分没掌握。sad...

题意:

      有n个城市,b个投票箱,(b>n), 已知每个城市的人数,让你怎样安排,能使投票箱装的票数的最大值最小。

解题思路:

       直接对每个投票箱能装下的投票量进行二分,最大值为城市中最多的人数(因为投票箱个数大于城市个数),最小值为0。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

int peo[510000];
int n,b;

bool iscan(int mid)
{
   int num=0;

   for(int i=1;i<=n;i++)
   {
      if(peo[i]%mid==0) //当前城市所用的箱子的最小个数
         num+=peo[i]/mid;
      else
         num+=peo[i]/mid+1; //每个城市的人,投票不能分开,
   }
   if(num>b) //如果箱子不够,则说明该箱子数量不行
      return false;
   return true;
}

int main()
{
   while(scanf("%d%d",&n,&b)!=EOF)
   {
      if(n==-1&&b==-1)
         break;

      int right=0;
      for(int i=1;i<=n;i++)
      {
         scanf("%d",&peo[i]);
         right=max(right,peo[i]);
      }

      int left=0,mid,ans;

      while(right>=left)
      {
         mid=(right+left)/2;
         if(iscan(mid))  //如果可以,能不能再放小点
         {
            ans=mid;
            right=mid-1;
         }
         else
            left=mid+1;  //不行的话,只能放大箱子的容量
      }
      printf("%d\n",ans);


   }

   return 0;
}

第三题:hdu 1710(Binary Tree Traversals) (基础数据结构)

       这题没做出来,对不起人。

题意:

      已知先序和中序遍历求后序遍历。

解题思路:

       通过先序遍历和中序遍历建一颗二叉树,然后递归按后序输出。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

int pre[1200],in[1200];
int n;

struct Tree
{
   int data;
   struct Tree * left,* right; //分别表示左孩子和右孩子
};

struct Tree * root;

struct Tree *build(int start1,int end1,int start2,int end2)
{
   if(end1<start1||end2<start2) //到了叶子节点,返回NULL
      return NULL;

   struct Tree * p =(struct Tree *)malloc(sizeof(struct Tree));

   int i;
   for(i=start2;i<=end2;i++) //在中序遍历中找到该点的位置
      if(pre[start1]==in[i])
            break;

   int num1=i-start2,num2=end2-i; //分别表示左子树和右子树的节点个数
   p->data=pre[start1];
   p->left=build(start1+1,start1+num1,start2,i-1); //建左孩子
   p->right=build(start1+num1+1,end1,i+1,end2);   //建右孩子

   if(start1==1) //记住一个根
      root=p;

   return p;
}

int cnt;

void prin(struct Tree * cur)
{
   if(cur->left) //后序遍历,先访问左孩子,后访问右孩子,最后输出当前值
      prin(cur->left);
   if(cur->right)
      prin(cur->right);
   if(cnt==0)  //注意格式,如果是第一个的话,就不输出空格
   {
      printf("%d",cur->data);
      cnt=1;
   }
   else
      printf(" %d",cur->data);

    return ;
}


int main()
{
   while(scanf("%d",&n)!=EOF)
   {
      for(int i=1;i<=n;i++)
         scanf("%d",&pre[i]);
      for(int i=1;i<=n;i++)
         scanf("%d",&in[i]);
      cnt=0;
      build(1,n,1,n);
      prin(root);
      putchar('\n');

   }
   return 0;
}


第四题:hdu 2964(Prime Bases)(简单数论模拟)

题意:

       以从2开始的质数为基底,分解数。

解题思路:

     类似于整数的拆分。

n = a0 + a1*p0 + a2*p0*p1 + a3*p0*p1*p2 + ...

先对p0求余,得a0, 然后除以p0,在对p1求余得a1,然后除以p1.。。。。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

bool prime[1100];
int prinum[220],coe[220][2],n; 

int Cal()
{
   int num=0,mul=1;

   int i=0;
   while(n)
   {
      ++i;
      if(n%prinum[i])
      {
         coe[++num][0]=n%prinum[i];//保存非零的系数
         coe[num][1]=i-1;  //保存位数
      }
      n/=prinum[i];
   }
   return num;
}

int main()
{
   memset(prime,true,sizeof(prime));
   prime[1]=false;
   int num=0;

   for(int i=2;i<=1000;i++)  //素数筛选法
      if(prime[i])
      {
         prinum[++num]=i;
         for(int j=i*2;j<=1000;j+=i)
            prime[j]=false;
      }

   while(scanf("%d",&n)&&n)
   {
      memset(coe,0,sizeof(coe));
      int flag=n;
      int temp=Cal();

      printf("%d = %d",flag,coe[1][0]);
      for(int i=1;i<=coe[1][1];i++)
         printf("*%d",prinum[i]);

      for(int i=2;i<=temp;i++)
      {
         printf(" + %d",coe[i][0]);
         for(int j=1;j<=coe[i][1];j++)
            printf("*%d",prinum[j]);
      }
      putchar('\n');
   }

   return 0;
}


第五题:hdu 2298 (Toxophily) (三分+二分)

        精度有点搞人。

题意:

       从坐标原点出发,速度一定,目标点确定,问以什么角度射击能够恰好击中目标。

解题思路:

       三分加二分。确定X,三分角度,找到最大的Y(应该满足凸性),如果y>Y,肯定不行。

找到最大的Y后,容易证明,在这个范围内,随着角度的增大,y的值也增大,满足单调性,用二分分出角度。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-10
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;


double x,y,v;

double Cal(double th)
{
   double t=x/(v*cos(th));

   return v*sin(th)*t-0.5*9.8*t*t;
}

int main()
{
   int t;

   scanf("%d",&t);
   while(t--)
   {
      scanf("%lf%lf%lf",&x,&y,&v);
      double left=0,right=PI/2,mid,mmid;
      double ans=-1,Max;

      while(right-left>eps)
      {
         mid=(right+left)/2;
         mmid=(mid+right)/2;

         double tempy1=Cal(mid);
         double tempy2=Cal(mmid);

         if(tempy1>tempy2)
         {
            Max=tempy1;
            right=mmid;
         }
         else
         {
            Max=tempy2;
            left=mid;
         }
      }
      if(y>Max)  //三分出最大的Y(Max)
         printf("-1\n");
      else
      {
         left=0,right=PI/2;
         while(right-left>eps)
         {
            mid=(right+left)/2;

            double tempy=Cal(mid);

            /*if(fabs(tempy-y)<1e-8) //这样写的话,不准确,如果进度控制不恰当的话,可能没有ans值,还是最后输出比较准确
            {
               ans=mid;
               break;
            }*/
            if(tempy<y)
               left=mid;
            else
               right=mid;

         }
         printf("%.6lf\n",left); //二分出角度,在这之间一定有解

      }

   }
   return 0;
}


第六题:hdu 4390 (Number Sequence) (组合数学+容斥原理)

       这题有点难度。

题意:

      给定b1---bn n个数,让你求出满足a1*a2*....*an-1*an(ai>1)=b1*b2*...*bn的种数。

解题思路:

      先对每一个b分解质因数,统计每个质因子的个数,将题目转化为将这么多的质因数分成n组的种数。

容斥原理+组合数学。

首先应明白 m个相同的数放进n个不同的盒子中的总的种数为C(n-1,n+m-1).运用隔板法:实际上就是把m个数分成n组,每组可以为空,为了使每组不为空,我们再加上n个数,这样一共就有了n+m-1个隔板,从中选出n-1个隔板,所以就有C(n-1,n+m-1)种。

对每一质因数的种数,运用上面的公式分别放进n个不同的盒子中,然后用乘法公式,这之中包括有些盒子为空的(不满足题目要求的),所以运用容斥原理减去放进(n-1)个不同盒子的情况(相当于有一个盒子一定为空)+(n-2)的情况。。。。。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

#define Max 1000000
#define Mod 1000000007
#define ll __int64

bool isprime[1100000];
int prime[110000],numpri=0;
int cnt[110000],C[130][130];

void divide(int b)
{
   for(int i=1;i<=numpri&&b>1;i++)
   {
      while(b%prime[i]==0)
      {
         cnt[i]++;  //相应的质因子个数加
         b/=prime[i];
      }

   }
   return ;
}

void Cal(int a,int b)
{
   C[0][0]=1;
   for(int i=1;i<=a;i++)
   {
      C[i][0]=1;
      for(int j=1;j<i;j++)
      {
         C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod; //将i个分成两组,第一组有一个,第二组有i-1个
      }                                 //当选择的j个包括第一个时,有C[i-1][j-1]种
      C[i][i]=1;                        //当选择的j个不包括第一个时,有C[i-1][j]种
   }
   return ;
}

int main()
{
   numpri=0;
   memset(isprime,true,sizeof(isprime));
   isprime[1]=false;
   for(int i=2;i<=Max;i++)   //质数筛选法
      if(isprime[i])
      {
         prime[++numpri]=i;
         for(int j=i*2;j<=Max;j+=i)
            isprime[j]=false;
      }
   //printf("%d\n",numpri); //78498 1000000内有78498个质因数
   Cal(110,110);//计算Ci,j
   int n;
   while(scanf("%d",&n)!=EOF)
   {
      memset(cnt,0,sizeof(cnt));
      int temp;
      for(int i=1;i<=n;i++)
      {
         scanf("%d",&temp);
         divide(temp);   //对每一个b分解质因数,只关心相应的质因数的个数
      }
      /*for(int i=1;i<=10;i++)
         if(cnt[i])
            printf("%d ",cnt[i]);*/
      ll ans=0;
      for(int i=0;i<n;i++)
      {
         ll temp0=C[n][i];//i表示盒子为空的个数,选择空盒子的位置
         for(int j=1;j<=numpri;j++)
            if(cnt[j])  //运用乘法原理
            {
               //printf("**%d ",cnt[j]);
               //printf("%d %d ",cnt[j]+n-i-1,n-i-1);
               temp0=(temp0*C[cnt[j]+n-i-1][n-i-1])%Mod;
            }
         if(i&1)  //此时为奇数   //运用容斥原理
            ans=(ans-temp0)%Mod;
         else
            ans=(ans+temp0)%Mod;
      }
      printf("%I64d\n",(ans+Mod)%Mod);
   }


   return 0;
}





有问题的欢迎评论交流。


 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值