NEFU要崛起——第1场

地址:http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=17581#problem/A

第一题:这题求用多少块边长为a的砖,能铺满n*m的广场,不能切割。。。直接除就行,比赛时大家基本都过了

代码:

#include<cstdio>
#include<iostream>
using namespace std;
long long n,m,a,s;
int main()
{
	while(cin>>n>>m>>a)
	{
		if(n%a)s=n/a+1;
		else s=n/a;
		if(m%a)s=s*(m/a+1);
		else s=s*(m/a);
		cout<<s<<endl;
	}
	return 0;
}

第二题:这题求n个人比赛的最终排名,得分相同的,最早超过该分数的人获胜,这题有很多坑,导致我错了好多次T_T

分析:假设最后答案为m。

1.一个人先獲得超m的分数,然后减到m以下,这时第二个人获得m分,然后前一个人加回m分,这样还算第一个人胜。。。

各种坑啊,然后我就记录第i时间,该时间相应的人,当时的得分,最后扫描一遍,分数最先超过m,且最终分数为m的人就是winner

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int mm=1111;
char name[mm][44],str[44];
int s[mm],r1[mm],r2[mm];
int i,j,a,n,m,ms;
int find()
{
	for(i=0;i<m;++i)
		if(!strcmp(str,name[i]))return i;
	s[m]=0;
	strcpy(name[m],str);
	return m++;
}
int main()
{
	while(~scanf("%d",&n))
	{
		j=m=0;
		while(n--)
		{
			scanf("%s%d",str,&a);
			i=find();
			s[i]+=a;
			r1[j]=i,r2[j++]=s[i];
		}
		for(ms=i=0;i<m;++i)
			ms=max(ms,s[i]);
		i=0;
		while(j--)
			if(s[r1[j]]==ms&&r2[j]>=ms)i=r1[j];
		puts(name[i]);
	}
	return 0;
}
第三题:这题就是进制转化的问题,而且只需要转化一维

分析:这题纯字母转数字大家应该都会吧,就是比如ADE=((A-A+1)*26+(D-A+1))*26+(E-A+1),关键是对于数字怎样转成字母的,这个有个小坑,不是直接%26就行的,因为会出现0,而0需要向前借一位,也就是说0代表Z,其实我也是猜的= =

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char s[1111],tmp[1111];
int n;
bool isnum(char c)
{
	return c>='0'&&c<='9';
}
bool check()
{
	for(int i=1;i<strlen(s);++i)
		if(isnum(s[i-1])&&!isnum(s[i]))return 0;
	return 1;
}
void change1()
{
	int i=0,a=0,b=0;
	while(!isnum(s[i]))
	{
		a=a*26+s[i]-'A'+1;
		++i;
	}
	while(i<strlen(s))
	{
		b=b*10+s[i]-'0';
		++i;
	}
	sprintf(s,"R%dC%d",b,a);
}
void decode(int a)
{
	int i=0,m=0;
	while(a>0)
	{
		tmp[m]=a%26;
		a/=26;
		if(!tmp[m])tmp[m]=26,--a;
		++m;
	}
	for(i=0;i<m>>1;++i)
		swap(tmp[i],tmp[m-i-1]);
	for(i=0;i<m;++i)tmp[i]+='A'-1;
	tmp[m]='\0';
}
void change2()
{
	int i=1,a=0,b=0;
	while(isnum(s[i]))
	{
		b=b*10+s[i]-'0';
		++i;
	}
	++i;
	while(i<strlen(s))
	{
		a=a*10+s[i]-'0';
		++i;
	}
	decode(a);
	sprintf(s,"%s%d",tmp,b);
}
int main()
{
	while(~scanf("%d",&n))
	{
		while(n--)
		{
			scanf("%s",s);
			if(check())change1();
			else change2();
			puts(s);
		}
	}
	return 0;
}

第四题:这题求一条路径上的数,使得这些数相乘后后边的0的个数最少

分析:这题一看就是DP问题,但是需要数学的一点分析,当时我一看就吓到了(数学弱渣T_T),然后去做E题了

对于表格中有0的情况,答案最多为1,路径便是通过这个点的路径;或者为0;咱们先抛弃这种情况不谈,把0当作10处理

然后我们发现,后导0只能从2*5得到(10也被分解成2*5了),也就是说对于一条路径上的数都做因数分解,最后的后导0即,min(2的个数,5的个数)

那么原问题转化为求一条路径使得min(2的个数,5的个数)最小;仔细思考,我们发现可以分开求,也就是说不管5有几个的情况下,使得2最少,另一个类似

这样答案就是最少的2,或最少的5,取最小即可,这个DP很简单,就详说了

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int mm=1111;
int a[mm][mm],b[mm][mm],c[mm][mm],s[mm][mm];
char p[mm][mm],out[mm*2];
int i,j,k,n,ans;
void zero()
{
	for(i=1;i<=n;++i)
		for(j=1;j<=n;++j)
			if(!a[i][j])return;
}
void oout(int x,int y)
{
	if(x==1&&y==1)return;
	if(p[x][y]=='R')oout(x,y-1);
	else oout(x-1,y);
	out[k++]=p[x][y];
}
int main()
{
	while(~scanf("%d",&n))
	{
		ans=1e9;
		for(i=1;i<=n;++i)
			for(j=1;j<=n;++j)
			{
				scanf("%d",&a[i][j]);
				if(a[i][j])
				{
					b[i][j]=c[i][j]=0;
					while(!(a[i][j]%2))
						++b[i][j],a[i][j]>>=1;
					while(!(a[i][j]%5))
						++c[i][j],a[i][j]/=5;
				}
				else b[i][j]=c[i][j]=ans=1;
			}
		if(ans==1)
		{
			zero();
			k=0;
			for(j=1;j<i;++j,++k)out[k]='D';
			for(j=1;j<n;++j,++k)out[k]='R';
			for(;i<n;++i,++k)out[k]='D';
			out[k]='\0';
		}
		memset(s,10,sizeof(s));
		s[0][1]=s[1][0]=0;
		for(i=1;i<=n;++i)
			for(j=1;j<=n;++j)
				if(s[i-1][j]<s[i][j-1])s[i][j]=s[i-1][j]+b[i][j],p[i][j]='D';
				else s[i][j]=s[i][j-1]+b[i][j],p[i][j]='R';
		if(s[n][n]<ans)ans=s[n][n],k=0,oout(n,n);
		memset(s,10,sizeof(s));
		s[0][1]=s[1][0]=0;
		for(i=1;i<=n;++i)
			for(j=1;j<=n;++j)
				if(s[i-1][j]<s[i][j-1])s[i][j]=s[i-1][j]+c[i][j],p[i][j]='D';
				else s[i][j]=s[i][j-1]+c[i][j],p[i][j]='R';
		if(s[n][n]<ans)ans=s[n][n],k=0,oout(n,n);
		printf("%d\n",ans);
		puts(out);
	}
	return 0;
}

第五题:这题给你三个点,求以这三个点为其三个顶点的多边形,这样的多边形的最小可能面积

分析:这题一看好像没办法搞,不过仔细想想便知道以这三个点画圆,可以求出圆心,进而求出这三个点的旋转角度,还有三个点划分出的三个弧度角

然后我们可以枚举多边形是几边行(由于边数越多,越接近圆,面积也越大,所以从小开始枚举,满足就退出),n满足的条件即,2*pi/n这个为每个小块的弧度,这个弧度a刚好是三个弧度角的约数(近似值就行),这时候该多边形就是n边形,而一个小块的面积s为r*r*sin(a)/2,总面积就是n乘以s。。。

代码:

#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
double ax,ay,bx,by,cx,cy,r,a1,a2,a3,ax1,ay1,bx1,by1,cx1,cy1,t1,t2,t3,x,y,ans;
double dis(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool ok(double aa)
{
    int tmp;
    tmp=(int)(t1/aa);
    if(fabs(tmp*aa-t1)>1e-6&&fabs((tmp+1)*aa-t1)>1e-6)return 0;
    tmp=(int)(t2/aa);
    if(fabs(tmp*aa-t2)>1e-6&&fabs((tmp+1)*aa-t2)>1e-6)return 0;
    tmp=(int)(t3/aa);
    if(fabs(tmp*aa-t3)>1e-6&&fabs((tmp+1)*aa-t3)>1e-6)return 0;
    return 1;
}
int main()
{
    while(~scanf("%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy))
    {
        ax1=ax,ay1=ay;
        bx1=bx,by1=by;
        cx1=cx,cy1=cy;
        a1=atan2(by-ay,bx-ax)+acos(-1.0)/2;
        a2=atan2(cy-by,cx-bx)+acos(-1.0)/2;
        ax=(ax+bx)/2,ay=(ay+by)/2;
        bx=(cx+bx)/2,by=(cy+by)/2;
        r=(sin(a2)*(ax-bx)+cos(a2)*(by-ay))/(sin(a1)*cos(a2)-sin(a2)*cos(a1));
        x=ax+r*cos(a1),y=ay+r*sin(a1);
        a1=atan2(ay1-y,ax1-x);
        a2=atan2(by1-y,bx1-x);
        a3=atan2(cy1-y,cx1-x);
        if(a1<0)a1+=2*acos(-1.0);
        if(a2<0)a2+=2*acos(-1.0);
        if(a3<0)a3+=2*acos(-1.0);
        if(a1>a2)swap(a1,a2);
        if(a1>a3)swap(a1,a3);
        if(a2>a3)swap(a2,a3);
        t1=a2-a1,t2=a3-a2,t3=2*acos(-1.0)-a3+a1;
        for(int i=3;i<1e6;++i)
        {
			a1=2*acos(-1.0)/i;
			if(ok(a1))break;
		}
        r=dis(x,y,ax1,ay1);
        ans=(2*acos(-1.0)/a1)*(sin(a1)*r*r/2);
        printf("%.8lf\n",ans);
    }
    return 0;
}

第六题:好吧,我还没调出来。。。。希望有人提供题解,以后提供题解被采用也加分^_^



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值