10.8 p.m.

T1:问题 A: 生日快乐

题目描述

windy 的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕。

现在包括 windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋糕。

windy 主刀,每一切只能平行于一块蛋糕的一边(任意一边),并且必须把这块蛋糕切成两块。

这样,要切成 N 块蛋糕,windy 必须切 N−1 次。

为了使得每块蛋糕看起来漂亮,我们要求 N 块蛋糕的长边与短边的比值的最大值最小。

你能帮助 windy 求出这个比值么?

输入

一行三个整数 X,Y,N

输出

一行一个浮点数,保留6位小数。

样例输入

5 5 5

样例输出

1.800000

提示


对于 100%的数据,满足 1≤X,Y≤10^4,  1≤N≤10。

题解

这道题可以用搜索的方法做。考虑dfs(x,y,k)表示返回在长为x,宽为y,要分k个人份所得最小的最大值。首先k=1时直接返回当前矩形的信息。否则枚举1到k-1,分别按照x和y方向切一刀,并且恰好分成i和k-i人份,注意横纵坐标要定为精度类型,才可以k等分。最后不要忘记保留6位小数(double自动显示6位小数)。

参考代码

#include<cstdio>
#define EXP 0.0000001
using namespace std;
double maxn=999999999.0,x,y,n;
double max_d(double p,double q)
{ return p-q>EXP?p:q; }
double min_d(double p,double q)
{ return p-q<EXP?p:q; }
double dfs(double x,double y,double k)
{
	if(k==1) return max_d(x,y)/min_d(x,y);
	double ret=maxn;
	for(double i=1;i<k;i++)
	{
		ret=min_d(ret,max_d(dfs(x/k*i,y,i),dfs(x/k*(k-i),y,k-i)));
		ret=min_d(ret,max_d(dfs(x,y/k*i,i),dfs(x,y/k*(k-i),k-i)));
	}
	return ret;
}
int main()
{
	scanf("%lf%lf%lf",&x,&y,&n);
	printf("%.6lf",dfs(x,y,n));
	return 0;
}

T2:问题 B: 最长距离

题目描述

windy有一块矩形土地,被分为 N*M 块 1*1 的小格子。 有的格子含有障碍物。 如果从格子A可以走到格子B,那么两个格子的距离就为两个格子中心的欧几里德距离。 如果从格子A不可以走到格子B,就没有距离。 如果格子X和格子Y有公共边,并且X和Y均不含有障碍物,就可以从X走到Y。 如果windy可以移走T块障碍物,求所有格子间的最大距离。 保证移走T块障碍物以后,至少有一个格子不含有障碍物。

输入

第一行包含三个整数,N M T。 接下来有N行,每行一个长度为M的字符串,'0'表示空格子,'1'表示该格子含有障碍物。

输出

包含一个浮点数,保留6位小数。

样例输入

3 3 0

001

001

110

样例输出

1.414214

提示


输入 #2 
4 3 0
001
001
011
000
输出 #2
3.605551

 


输入 #3 
3 3 1
001
001
001
输出 #3 
2.828427

 


说明/提示

 


20%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 0 。
40%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 2 。
100%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 30 。

题解

这道题首先要把字符串转化成地图。由于题目中给出了t,因此考虑逆向思维,从一个点往外搜索到每一个点,观察需要最少移走多少障碍物。因此就把这道题转化成了求最短路的问题。两个直接相连的点就可以作为边。如果通向的位置有障碍物,边权为1,反之为0。然后直接mn扫描所有点,如果距离d(表示至少需要移走多少)小于等于t,表示可以取到这两个点之间的距离,就更新一下答案。因此总算法为mn枚举每一个点,并以当前点为初始点跑SPFA,最后再nm统计答案即可,总复杂度(m^2n^2)

参考代码

#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
int n,m,t;
int dx[5]={0,0,1,0,-1};
int dy[5]={0,1,0,-1,0};
struct node
{
	int x,y;
};
char s[100];
int e_map[50][50],vis[50][50],d[50][50],maxn=0;
queue<node>q;
bool pd(int x,int y)
{ return (x>0)&&(x<=n)&&(y>0)&&(y<=m); }
void SPFA(int x,int y)
{
	memset(d,0x3f,sizeof(d));
	node pt;
	pt.x=x;pt.y=y;
	q.push(pt);vis[x][y]=1;d[x][y]=e_map[x][y];
	while(!q.empty())
	{
		node k=q.front();q.pop();
		vis[k.x][k.y]=0;
		for(int i=1;i<=4;i++)
		{
			int x1=k.x+dx[i];
			int y1=k.y+dy[i];
			if(!pd(x1,y1)) continue;
			if(d[x1][y1]>d[k.x][k.y]+e_map[x1][y1])
			{
				d[x1][y1]=d[k.x][k.y]+e_map[x1][y1];
				if(!vis[x1][y1])
				{
					vis[x1][y1]=1;
					node tt;
					tt.x=x1;tt.y=y1;
					q.push(tt);
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		for(int j=1;j<=m;j++)
		e_map[i][j]=s[j-1]-'0';
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			SPFA(i,j);
			for(int x=1;x<=n;x++)
			{
				for(int y=1;y<=m;y++)
				{
					if(d[x][y]<=t)
					{
						if((x-i)*(x-i)+(y-j)*(y-j)>maxn)
						{
							maxn=(x-i)*(x-i)+(y-j)*(y-j);
						}
					}
				}
			}
		}
	}
	printf("%.6lf",sqrt((double)maxn));
	return 0;
}

T3:问题 C: Windy 数

题目描述

Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 Windy 数。

Windy 想知道,在 A 和 B 之间,包括 A 和 B,总共有多少个 Windy 数?

输入

一行两个数,分别为 A,B。

输出

输出一个整数,表示答案。

样例输入

1 10

样例输出

9

提示


【样例输入 2】

25 50

【样例输出 2】

20
【数据范围与提示】

20% 的数据,满足 1≤A≤B≤10^6 ;

100% 的数据,满足 1≤A≤B≤2\times 10^9

题解

这是一道经典的数位dp的问题。首先从0到9都是windy数,然后10就不是,如果第二位为1,那么第一位只能取3到9,同理第二位取1到9都可以由一位数转移而来。三位数又可以由两位数转移……因此可以提前预处理出以某个数字开头的所有小于等于这个整数的windy数的个数。注意0也要加入到转移中,因为可能出现12034这种情况,0不在开头但在中间。同时为了计算方便,我们定义dp[i][10]表示第i-1位数所有windy数的总和,方便后面的计算。然后对于一个数,从最高位枚举到最低位。如果某一位为5,那么这一位可以取4,3,2,1,0.然后如果不与前面的一位数字冲突就可以把从这一位开始到最后全部记入答案(这一点直接在初始化中解决)。稍微需要注意的是最高位和最低位,最高位可以取到10(表示更低位的所有windy数),然后最低位要单独来(不用再推到下一位了),主要是不要漏掉0。

参考代码

#include<cstdio>
#define LL long long
using namespace std;
LL a,b,dp[20][20],nus[50];
LL abs1(LL p) { return p>0?p:-p; }
void init()
{
	for(int i=0;i<=9;i++) dp[1][i]=1;
	dp[1][10]=1;
	for(int i=2;i<=10;i++)
	{
		for(int j=0;j<=9;j++)
		  for(int k=0;k<=9;k++)
		    if(abs1(j-k)>=2)
			  dp[i][j]+=dp[i-1][k];
		for(int k=1;k<=9;k++) dp[i][10]+=dp[i-1][k];
		dp[i][10]+=dp[i-1][10];
	}
}
LL figure(LL p)
{
	if(p==0) return 1;
	LL len=0,p1=p,ans=0;
	while(p1)
	{
		len++;p1/=10;
	}
	for(int i=1;i<=len;i++) nus[i]=p%10,p/=10;
	for(int i=len;i>=1;i--)
	{
		if(i==len)
		{
			for(int j=nus[i]-1;j>=1;j--)
			  ans+=dp[i][j];
			ans+=dp[i][10];
		}
		else if(i!=1)
		{
			for(int j=nus[i]-1;j>=0;j--)
			  if(abs1(j-nus[i+1])>=2)
				  ans+=dp[i][j];
			if(abs1(nus[i]-nus[i+1])<2) break;	  		  
		}
		else
		{
			for(int j=nus[i];j>=0;j--)
			  if(abs1(j-nus[i+1])>=2)
				ans+=dp[i][j];		
		}
	}
	return ans;
}
int main()
{
	scanf("%lld%lld",&a,&b);
	init();
	printf("%lld",figure(b)-figure(a-1));
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值