2018SCUACM Training 4 校赛练习赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/w865629524/article/details/79977338

哈哈哈勤奋的我

今天终于把training 4补完了

但是emmmm感觉training4的技巧性好强啊

很多东西都没用过

所以我不知道能不能讲清楚

emmmm我尽量?

题目链接:2018SCUACM Training 4 校赛练习赛

------------------------------------------------------分割线-----------------------------------------------------

论写博客两个多小时网页闪退没自动保存的痛????

心态崩了

本来觉得写的挺好的。。。第二次写估计写不了第一次那么好了。。。。凑活凑活吧。。。

以下内容都是第二次写。。

A - Circular Area

 
Your task is to write a program, which, given two circles, calculates the area of their intersection with the accuracy of three digits after decimal point.
Input
In the single line of input file there are space-separated real numbers x1 y1 r1 x2 y2 r2. They represent center coordinates and radii of two circles.
Output
The output file must contain single real number - the area.
Sample Input
20.0 30.0 15.0 40.0 30.0 30.0
Sample Output
608.366


题目大意:给出两个圆的圆心和半径,求两个圆重合部分的面积,

显然这是个数学题emmmm但是数学上的东西在这里不做详细讨论(我觉得高中能毕业的人都能掌握这些吧)

首先我们考虑两个圆的相对位置关系:相离,外切,相交,内切,内含

由于是算相交部分的面积,所以相离和外切时只要输出0就好了。内切和内含时直接输出小圆的面积就好了。

剩下相交的情况,怎么处理呢?相信你的小学老师一定教过你一个逼格非常高的方法叫做割补法,就是补上一块能求出的面积,把一块不能求或者不方便求的面积划分成若干块可求的面积。

我们假设两个圆的交点为P,Q,圆心为O1,O2,则相交部分的面积可以表示为:扇形PQO1+扇形PQO2-四边形PO1QO2

求扇形面积需要求角度,角度可以用余弦定理求,结合math.h里面的acos函数,即反三角函数即可。

顺便说一下, 好像C语言的规范是float和double在读入的时候分别要用%f和%lf,但在输出的时候都要用%f,只是后来double的输出也可以兼容%lf。就比如这题我用%lf怎么都是wa,改了个%f就A了。

//%lf输出会wa 
#include<cstdio>
#include<cmath>
int main()
{
  double x1,y1,r1,x2,y2,r2,d,ans;
  const double pi = 3.141592653;
  while(scanf("%lf%lf%lf%lf%lf%lf",&x1,&y1,&r1,&x2,&y2,&r2) != EOF)
  {
    d = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    if(d >= r1+r2)
      ans = 0;
    else if(fabs(r1-r2) >= d)
    {
      double r = r1 < r2 ? r1 : r2;
      ans = pi*r*r;
    }
    else
    {
      double a, b;
      a = acos((r1*r1+d*d-r2*r2)/(2*r1*d));
      b = acos((r2*r2+d*d-r1*r1)/(2*r2*d));
      ans = a*r1*r1+b*r2*r2-d*r1*sin(a);
    }
    printf("%.3f\n",ans);
  }
  return 0;
}

B - Coins

 

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch. 

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
InputThe input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.OutputFor each test case output the answer on a single line.Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8
4

题意:有n种硬币,第i种硬币价值为a[i],数量为c[i],问在不找钱的情况下,在价值不超过m的价格中能凑出多少种不同的价格。

多重背包+二进制优化

看到题目就会有一种非常亲切的感觉——背包问题

n种物品选择,有数量限制,显然是一个多重背包问题。

通常我们处理多重背包问题时,可以考虑将其转换为完全背包问题和01背包问题来考虑。

我们可以开一个数组dp,dp[i]记录在不超过i的价值中,能凑出的最大价值(所以dp[i]=i说明能恰好凑出i的价值)

(我知道这句话可能有点拗口,但是我找不到更好的表达,而且我觉得这句话深究起来实际上还是错的,不过也能勉强将就一下啦。。。emmmm所以还是自己理解吧,反正只要记住一句话:dp[i]=i表示在已有的硬币中能凑出i的价值)

我们整个程序的主体部分只做了一件事,就是建立这个dp数组。我们先考虑状态转移方程,即在什么情况下能实现dp[i]=i呢?在使用一个价值为value的硬币(或者是使用一堆硬币总的价值为value时),要达到dp[i]=i的前提就是已经在之前使用的硬币中达到了dp[i-value]=i-value,所以这就对应我们的状态转移方程:dp[i]=max(dp[i], dp[i-value]+value)

好了,接下来我们实际解决这个问题。。。。既然是背包问题,我们就遵循背包问题的基本思路:先考虑前i种硬币可能组成的价值,再在其中加入第i+1种硬币。

加入i+1种硬币时可能有两种情况:a[i]*c[i]>=m和a[i]*c[i]<m,也就分别对应我们的完全背包和01背包。

既然a[i]*c[i]>=m了,那限制a[i]的硬币拿c[i]个和让a[i]的硬币可以拿无限个有区别吗?显然没有,所以这就是完全背包问题了。假设我们拿了k个这种硬币,那么对应的状态转移方程为:dp[j]=max(dp[j], dp[j-k*a[i]]+k*a[i])。然而对于完全背包问题,我们是不需要遍历这个k的。为什么呢?因为我们可以先处理使用好使用i个硬币的情况,再在这个基础上再使用一枚硬币,这不就是使用i+1枚硬币的情况吗?所以对应的函数为:(不懂完全背包的童鞋仔细想想为什么不考虑k)

void complete_bag(int value)
{
	for(int i = value; i <= m; i++)
		dp[i] = max(dp[i], dp[i-value]+value);
}

那么当a[i]*c[i]<m的情况我们该怎么办呢?我们只能枚举使用1枚,2枚,直到使用c[i]枚硬币的所有情况,在使用k枚硬币时对应的状态转移方程与完全背包的状态转移方程相同,只是对k的取值有了限制。对应的代码为:

for(int i = 1; i <= c[i]; i++)
{
	for(int j = m; j >= i*a[i]; j--)
		dp[j] = max(dp[j], dp[j-a[i]] + a[i]);
}

(思考一下为什么完全背包问题中循环的写法是i++,而这里是j--?这种写法能不能改?)

(我都这么问了肯定就不能改了对吧emmmm,因为完全背包问题对数目没有限制,所以对于取k个硬币的状态k,可以从状态k-1中再取一个硬币转换而来,所以只要先更新完前面的,后面的就可以直接更新了。而01背包问题中,取k-1个和取k个是相互独立的,需要单独计算)

(我知道我这里又没说清楚emmmm如果读3遍还读不通的话就自己理解吧。。。不然看我这段话容易跑偏emmmm)

然后我们发现了一个很严肃的问题:第二个循环是O(m)的,这两层循环是O(m*c[i])的,而这样的循环我们要做n次,所以总的复杂度是O(nm*max(c[i]))的emmmm也就是1e2*1e5*1e3=1e10就炸了(虽然看起来好像是个不高不低的复杂度,我也没试过,我猜会T把)

所以在这里可以采取一点优化策略:二进制优化。二进制优化使用了一个结论:由2^0,2^1,2^2,...,2^k可以表示出1到2^(k+1)-1范围内的任意整数

所以我们想枚举1到c[i]的所有整数,只需要将c[i]表示为2^k-1+p的形式即可,其中2^k-1部分可以采用二进制优化,只需要枚举k个数即可(把2^k-1个硬币看成分别由2^0个,2^1个,2^2个...2^(k-1)个原来的硬币组成的k个不同的硬币),剩余的p相对就比较小了,可以直接逐个枚举。

对应的代码为:

void zeroone_bag(int value)
{
	for(int i = m; i >= value; i--)
		dp[i] = max(dp[i], dp[i-value]+value);
}
for(int i = 1; i <= c[idx]; i <<= 1)//二进制优化
{
	zeroone_bag(i*a[idx]);
	c[idx] -= i;
}
if(c[idx])
	for(int i = 1; i <= c[idx]; i++)
		zeroone_bag(a[idx]);

好了总结一下:这题显然是个多重背包问题,我们把它分解为完全背包问题和01背包问题来解决,其中01背包问题采用了二进制优化,dp[i]表示不超过i的价值中能凑出的最大价值,dp[i]=i表示价值i能被凑出来,状态转移方程为dp[i]=max(dp[i], dp[i-value]+value)

(其实总结这一段第一次写写的更好,但是我忘了怎么写的了。。。以后写博客一定要记得手动保存。)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 105
#define maxm 100005
using namespace std;
int n,m;
int a[maxn],c[maxn];
int dp[maxm];//dp[i]:能否凑出i元
void complete_bag(int value)
{
	for(int i = value; i <= m; i++)
		dp[i] = max(dp[i], dp[i-value]+value);
}
void zeroone_bag(int value)
{
	for(int i = m; i >= value; i--)
		dp[i] = max(dp[i], dp[i-value]+value);
}
void multiple_bag(int idx)
{
	if(a[idx]*c[idx] >= m)//完全背包问题
		complete_bag(a[idx]);
	else//01背包问题
	{
		for(int i = 1; i <= c[idx]; i <<= 1)//二进制优化
		{
			zeroone_bag(i*a[idx]);
			c[idx] -= i;
		}
		if(c[idx])
			for(int i = 1; i <= c[idx]; i++)
				zeroone_bag(a[idx]);
	}
}
int main()
{
	while(scanf("%d%d",&n,&m) != EOF && (n || m))
	{
		memset(dp,0,sizeof(dp));
		for(int i = 0; i < n; i++)
			scanf("%d",&a[i]);
		for(int i = 0; i < n; i++)
			scanf("%d",&c[i]);
		for(int i = 0; i < n; i++)
			multiple_bag(i);
		int ans = 0;
		for(int i = 1; i <= m; i++)
			if(dp[i] == i)
				ans++;
		printf("%d\n",ans);
	}
	return 0;
}

C - Number Sequence

 
A number sequence is defined as follows: 

f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7. 

Given A, B, and n, you are to calculate the value of f(n). 
InputThe input consists of multiple test cases. Each test case contains 3 integers A, B and n on a single line (1 <= A, B <= 1000, 1 <= n <= 100,000,000). Three zeros signal the end of input and this test case is not to be processed. 
OutputFor each test case, print the value of f(n) on a single line. 
Sample Input
1 1 3
1 2 10
0 0 0
Sample Output
2
5


好了终于来到了这个简单题。

看到这题目第一眼,你一定会想到周期对吧!

所以最坏的打算打表验证就好了。如果你想通过数学验证的话,可以这么看:

A和B都是定值,所以f(n)的取值就取决于f(n-1)与f(n-2)的组合。显然f(n)的值域为0到6共7种可能,所以其组合的种类有49,那么49一定是一个周期。至于有没有更小的周期,我们不需要考虑也没法考虑,只知道即便有更小的周期,也一定是49的因数就是了。

这题目数据还有点弱,其实题面上还有个陷阱,就是当A和B都是7的倍数时,f是没有周期的。因为只有前面两个是1,后面全0了。不过数据弱所以我还是A了。。。代码也没改了,自己知道就行了。

//陷阱:a%7 = b%7 = 0时无周期,因为前两个是1,后面全部是0
//A了因为数据弱 

#include<cstdio>
int main()
{
  int a,b,n;
  int f[55];
  while(scanf("%d%d%d",&a,&b,&n) && (a||b||n))
  {
    a %= 7;
    b %= 7;
    f[1] = f[2] = 1;
    for(int i = 3; i <=50; i++)
      f[i] = (a*f[i-1]%7+b*f[i-2]%7)%7;
    printf("%d\n",f[n%49]);
  }
  return 0;
}

D - Pashmak and Buses

 

Recently Pashmak has been employed in a transportation company. The company has kbuses and has a contract with a school which has n students. The school planned to take the students to d different places for d days (each day in one place). Each day the company provides all the buses for the trip. Pashmak has to arrange the students in the buses. He wants to arrange the students in a way that no two students become close friends. In his ridiculous idea, two students will become close friends if and only if they are in the same buses for all d days.

Please help Pashmak with his weird idea. Assume that each bus has an unlimited capacity.

Input

The first line of input contains three space-separated integers n, k, d (1 ≤ n, d ≤ 1000; 1 ≤ k ≤ 109).

Output

If there is no valid arrangement just print -1. Otherwise print d lines, in each of them print n integers. The j-th integer of the i-th line shows which bus the j-th student has to take on the i-th day. You can assume that the buses are numbered from 1 to k.

Examples
Input
3 2 2
Output
1 1 2 
1 2 1 
Input
3 2 1
Output
-1
Note

Note that two students become close friends only if they share a bus each day. But the bus they share can differ from day to day.


题目大意:n个人乘k辆车玩d天,车的容量不限,求出乘车方案使得d天内没有任何两个人是一直坐同一辆车的。

我们考虑如何安排两个人不坐同一辆车呢?先假设他们d天一直坐的同一辆车,那么要让他们分开,只需要有一天他们不坐一起就行了。

不妨列出来看一下,从最简单的情况开始,假设第一个人d天内的坐车方案为1 1 1 ...1 1(d个1,1表示车的编号),那第二个人因为不能和第一个人一起,所以可以让他在最后一天换车,即坐车方案为1 1 1 ... 1 2,同理,第三个人也要换,即1 1 1 1 ... 1 3,到了第k个人,他的乘车方案就是1 1 1 ... 1 k,好了问题来了,没有第k+1辆车,那第k+1个人该怎么办呢?没错,相信你一定能想到,就是在倒数第二天让他换车不就行了嘛!即乘车方案为1 1 1 ... 2 1

举个特例:k=3,d=2,那么每个人可能的乘车方案为:

1 1

1 2

1 3

2 1

2 2

2 3

3 1

3 2

3 3

看出来什么没有?如果没看出来,把每个数都减一个1

0 0

0 1

0 2

1 0

1 1

1 2

2 0

2 1

2 2

这不就是我们的三进制数吗?所以k辆车d天的不同乘车方案一共有k^d种,如果人数小于这个数,则可以安排,否则就不能。而且每种排法对应了一个d位k进制数

输出的时候要注意:一个k进制数对应的是一个人d天的乘车方案,而输出的时候要求一行输出n个人的乘车方案

在这里还要特殊处理一组数据:k=1时,显然没有1进制数嘛对吧!这个时候单独拿出来就行了!

#include<cstdio>
#include<cstring>
#define maxn 1005
#define maxd 1005
using namespace std;
int n,k,d;
int bus[maxn][maxd];
void trans(int i)
{
	int t = i, idx = 0;
	while(t)
	{
		bus[i][idx++] = t%k;
		t/=k;
	}
}
int main()
{
	while(scanf("%d%d%d",&n,&k,&d) != EOF)
	{
		int s = 1;
		for(int i = 0; i < d; i++)
		{
			s *= k;
			if(s >= n)
				break;
		}
		if(s < n)
			printf("-1\n");
		else if(k == 1 && n == 1)//没有1进制 
		{
			while(d--)
				printf("1\n");
		}
		else
		{
			memset(bus, 0, sizeof(bus));
			for(int i = 0; i < n; i++)//第i个人d天内的乘车方案 
				trans(i);
			for(int i = 0; i < d; i++)
			{
				for(int j = 0; j < n-1; j++)
					printf("%d ", bus[j][i]+1);
				printf("%d\n",bus[n-1][i]+1);
			}
		}
	}
	return 0;
}

E - Rescue

 

Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison. 

Angel's friends want to save Angel. Their task is: approach Angel. We assume that "approach Angel" is to get to the position where Angel stays. When there's a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards. 

You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.) 
InputFirst line contains two integers stand for N and M. 

Then N lines follows, every line has M characters. "." stands for road, "a" stands for Angel, and "r" stands for each of Angel's friend. 

Process to the end of the file. 
OutputFor each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing "Poor ANGEL has to stay in the prison all his life." 
Sample Input
7 8
#.#####.
#.a#..r.
#..#x...
..#..#.#
#...##..
.#......
........
Sample Output
13

又是一道非常经典的BFS迷宫板子题

但是这个迷宫和普通迷宫不同,他增加了守卫这一设定,击杀守卫要消耗额外的1单位时间。

在普通迷宫问题中,我们让每个结点入队后,可以保证以后不可能再出现比当前更近的到达这个结点的路径(因为本来就是按照步数从小到大入队的),所以可以通过判断是否访问过该结点来决定是否入队。

但在这个问题中,由于守卫的出现,导致已经访问过的结点很可能还要出现比他更近的路径,所以我们不是通过判断该结点是否被访问过来决定是否入队,而是判断能否缩短到达该结点的路径来判断。

除了这点以外,其余和BFS的板子题一毛一样。至于迷宫问题的BFS,隐约记得前两天写过一篇,所以就不想在这里赘述了emmmm直接上代码了。

#include<cstdio>
#include<cstring>
#define maxn 205
#define maxm 205
char map[maxn][maxm];
int book[maxn][maxm];//0表示起点,-1表示不可达点,其他正数表示到达该点的最小步数 
int quex[maxn*maxm];
int quey[maxn*maxm];
int n,m,x,y,dx,dy;
int head, tail;
int main()
{
	while(scanf("%d%d",&n,&m) != EOF)	
	{
		for(int i = 0; i < n; i++)
		{
			getchar();
			for(int j = 0; j < m; j++)
			{
				map[i][j] = getchar();
				if(map[i][j] == 'a')//起点 
				{
					x = i;
					y = j;
				}
				else if(map[i][j] == 'r')//终点 
				{
					dx = i;
					dy = j;
				}
			}
		}
		head = 0;
		tail = 1;
		memset(book,-1,sizeof(book));
		quex[0] = x;
		quey[0] = y;
		book[x][y] = 0;
		int nx[] = {0,1,0,-1};
		int ny[] = {1,0,-1,0};
		while(tail > head)//队列非空 
		{
			//出队 
			x = quex[head];
			y = quey[head];
			head++;
			for(int i = 0; i < 4; i++)
			{
				int tx = x+nx[i];
				int ty = y+ny[i];
				//地图越界 
				if(tx < 0 || tx >= n || ty < 0 || ty >= m)
					continue;
				//围墙 
				if(map[tx][ty] == '#')
					continue;
				int step = book[x][y] + 1;
				if(map[tx][ty] == 'x')
					step++;
				//更远的路径 
				if(book[tx][ty] != -1 && step >= book[tx][ty])
					continue;
				//入队 
				quex[tail] = tx;
				quey[tail] = ty;
				book[tx][ty] = step;
				tail++;
			}
		}
		if(book[dx][dy] == -1)
			printf("Poor ANGEL has to stay in the prison all his life.\n");
		else
			printf("%d\n",book[dx][dy]);
	}
	return 0;
}

F - 训练


Time Limit: 1000 MS    Memory Limit: 131072 K 


    

Description

为了使自己的球技不断提高,罗贝托必须每天训练。他手上有N份训练计划,每份训练计划正好会占用他一天的时间。罗贝托从第一天开始训练,他可以任意安排这些训练的顺序,第i份训练的能力提升为Pi,截止日期为Di。在截止日期后才完成的训练没有能力提升。请帮助罗贝托规划每天的训练,使他获得的能力值提升最大。

Input

输入包含多组数据。对于每组数据:第一行: 单个整:N,1<=N<=10^5第二行到第N+1行:第i+1行有两个整数:Di和Pi,1<=Di<=10^9, 1<=Pi<=10^9

Output

对于每组数据,输出一行,一个整数,表示能获得的最大的能力值。

Sample Input

32 101 51 7102 995 294 828 723 868 243 697 443 792 19

Sample Output

17515


又到了久违的中文题(为什么要说又???)

贪心无疑了。

我们首先考虑单份训练计划,他有很多天可以放进去,那放在哪天最好呢?当然是放在与其他训练计划冲突最小的那天!哪天与其他训练计划冲突最小呢?当然是这个计划可以放到的最后一天。

所以我们从最后一天开始考虑(为什么不从第一天?),考虑最后一天放哪个训练计划比较合适呢?想来想去,我决定放一个增长能力最多的训练计划!聪明吧!那放哪个最多的呢?当然只能放能安排在这一天的训练计划咯。。。所以我们要做的操作是:筛选出在第i天之后结束的所有训练计划,并且在其中找出最大值。

对没错就是堆了,也就是STL里面的priority_queue,我们不断的重复两个操作:1.把第i天之后才结束的训练计划加进去,2.在可以选择的训练计划当中拿出能力提升最大的,放在第i天。这样就是最贪心的

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define maxn 100005
using namespace std;
typedef struct
{
	int d,p;
} node;
bool cmp(node a, node b)
{
	return a.d > b.d;
}
node a[maxn];
priority_queue<int> que;
int main()
{
	long long n,max,date,index;
	while(scanf("%d",&n) != EOF)
	{
		for(int i = 0; i < n; i++)
			scanf("%d%d",&a[i].d,&a[i].p);
		sort(a,a+n,cmp);
		date = n;
		max = index = 0;
		while(date > 0)
		{
			while(a[index].d >= date)
				que.push(a[index++].p);
			if(!que.empty())
			{
				int t = que.top();
				que.pop();
				max += t;
			}
			date--;
		}
		while(!que.empty())
			que.pop();
		printf("%lld\n",max);
	}
	return 0;
}

G - Yukari's Birthday

 
Today is Yukari's n-th birthday. Ran and Chen hold a celebration party for her. Now comes the most important part, birthday cake! But it's a big challenge for them to place n candles on the top of the cake. As Yukari has lived for such a long long time, though she herself insists that she is a 17-year-old girl. 
To make the birthday cake look more beautiful, Ran and Chen decide to place them like r ≥ 1 concentric circles. They place k i candles equidistantly on the i-th circle, where k ≥ 2, 1 ≤ i ≤ r. And it's optional to place at most one candle at the center of the cake. In case that there are a lot of different pairs of r and k satisfying these restrictions, they want to minimize r × k. If there is still a tie, minimize r. 
InputThere are about 10,000 test cases. Process to the end of file. 
Each test consists of only an integer 18 ≤ n ≤ 10 12
OutputFor each test case, output r and k.Sample Input
18
111
1111
Sample Output
1 17
2 10
3 10


题目大意:蛋糕上要插n根蜡烛。插蜡烛的规则是这样的:必须插r个圆圈,并且第i个圆圈必须插k^i根蜡烛,然后蛋糕的圆心处可以插1根蜡烛或不插,这根蜡烛不算在r个圆圈里面。求满足条件r和k,如果有多组,优先取r*k更小的,如果还有多组,优先取r小的。

显然有一组解:r=1,k=n-1,也就是一个圆插n-1根蜡烛,圆心插1根蜡烛。

然后k>=2,k取2的时候,我们发现2^40>10^12,故r的取值范围就是1到40,这个范围很小了对吧emmmm所以r枚举无疑了

那k怎么办?不用说也知道是二分了。。。

但是二分有个很重要的细节,因为我们要算左边等式的和,但是你算下数据范围,会炸long long啊!所以直接pow是不行的,只能一个个乘。而且,我们要判断等式是否成立,其实就是判断k+k^2+...+k^r是否可以等于n或n-1,也就是说当乘到一半,我们发现左边已经大于n了,就直接跳过即可。

还有还有,就算一个个乘,还是有可能越界,所以有一个很牛逼的判断是否越界的方法。假设M就是界,a*b可能会越界,所以当然就不能通过判断a*b>M来判断是否越界了,那怎么办呢?改为判断a>M/b就行啦!

具体细节还是看代码吧

#include<cstdio>
#include<iostream>
#include<algorithm>
#define inf 0xfffffff
using namespace std;
typedef long long ll;
int main()
{
	ll n;
	while(cin>>n)
	{
		//通解r=1,k=n-1,即中间放一根,外面一个圆放n-1根 
		ll minr = 1, mink = n-1;
		for(ll r = 1; r <= 40; r++)
		{
			ll lk = 2, rk = n;
			while(lk <= rk)//二分 
			{
				ll midk = (lk+rk)/2;
				ll t = 1, sum = 0;//计算左边等式 
				for(ll i = 0; i < r; i++)
				{
					if(n/midk < t)//t马上超过n了(其实是t*mid可能炸long long) 
					{
						sum = n+1;//相当于告诉后面等式不可能成立 
						break;
					}
					t *= midk;
					sum += t;
					if(sum > n)//左边已经超过n了,等式不成立 
						break;
				}
				if(sum < n-1)
					lk = midk+1;
				else if(sum > n)
					rk = midk-1;
				else
				{
					if(r*midk < minr*mink)
					{
						minr = r;
						mink = midk;
					}
					rk = midk-1;
				}
			}
		}
		cout<<minr<<" "<<mink<<endl;
	}
	return 0;
}

H - Tempter of the Bone

 

The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze. 

The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him. 
InputThe input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following: 

'X': a block of wall, which the doggie cannot enter; 
'S': the start point of the doggie; 
'D': the Door; or 
'.': an empty block. 

The input is terminated with three 0's. This test case is not to be processed. 
OutputFor each test case, print in one line "YES" if the doggie can survive, or "NO" otherwise. 
Sample Input
4 4 5
S.X.
..X.
..XD
....
3 4 5
S.X.
..X.
...D
0 0 0
Sample Output
NO
YES

最后一题!

还是熟悉的迷宫问题!

判断能否【恰好】在T步的时候到达终点,而且已经去过的点不能回去

恰好就很烦了,可能步数太多的话我还要特意去绕路???

所以虽然这是迷宫问题,但是我们所求的是到达终点可能的步数而不是最短的步数,这时候BFS会显得有点累赘(因为去过的点不能回去),所以只能采用DFS了。

以往的迷宫问题不采用DFS一方面的原因是因为DFS搜索深度太深了,要遍历整个迷宫,也就是最坏的情况是O(2^(mn)),但是这里已经限制了:恰好在T步,也就是说最大深度就是T了,n,m<7,也就是迷宫大小也不大,所以情况还没那么糟(虽然非要算最坏情况的数量级的话还是会炸,但是想达到最坏情况也没那么容易,何况下面还有剪枝)

然后我们来考虑DFS剪枝的问题:首先是奇偶剪枝,也就是说如果这两个点之间的距离(准确的说叫曼哈顿距离)和可能绕路到达这里的步数一定是同奇偶的。其次,既然你要走T步,那么迷宫中就要预留至少T+1个位置给你对吧(包括起点),如果不满足这个条件,也可以cut掉。最后,T步要到达终点,那T绝对是要大于这两个点之间的距离的。

有这三个强有力的剪枝,也就让这个题目的DFS实现变得可行了。

#include<iostream>
#include<cstring>
#include<cmath>
#define inf 0xffffff
#define maxn 10
#define maxm 10
using namespace std;
char map[maxn][maxm];
int book[maxn][maxm];
int n,m,t;
int sx, sy, ex, ey;
int dfs(int x, int y, int step) {
	if(map[x][y] == 'D')
	{
		if(step == 0)
			return 1;
		return 0;
	}
	int dx[] = {1,0,-1,0};
	int dy[] = {0,1,0,-1};
	int t = 0;
	for(int i = 0; i < 4; i++) {
		int nx = x+dx[i];
		int ny = y+dy[i];
		if(nx < 0 || nx >= n
		|| ny < 0 || ny >= m
		|| book[nx][ny] != 0
		|| map[nx][ny] == 'X')
			continue;
		book[nx][ny] = 1;
		t = dfs(nx,ny,step-1);
		if(t) return 1;
		book[nx][ny] = 0;
	}
	return 0;
}
int main() {
	while(cin>>n>>m>>t) {
		if(!(n || m || t))
			break;
		int wallCnt = 0;
		for(int i = 0; i < n; i++)
		for(int j = 0; j < m; j++) {
			cin>>map[i][j];
			if(map[i][j] == 'S') {
				sx = i;
				sy = j;
			}
			else if(map[i][j] == 'D') {
				ex = i;
				ey = j;
			}
			else if(map[i][j] == 'X')
				wallCnt++;
		}
		if(abs(sx-ex)+abs(sy-ey) < t)//距离剪枝 
			cout<<"NO"<<endl;
		else if((abs(sx-ex)+abs(sy-ey)+t) & 1)//奇偶剪枝
			cout<<"NO"<<endl;
		else if(m*n-wallCnt <= t)//数量剪枝
			cout<<"NO"<<endl;
		else {
			memset(book,0,sizeof(book));
			book[sx][sy] = 1;
			int step = dfs(sx,sy,t);
			book[sx][sy] = 0;
			if(step) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
	}
	return 0;
}



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页