您的位置:首页 > 大数据 > 人工智能

【解题报告】2016 Multi-University Training Contest 3

2016-09-09 12:28 573 查看
题目链接

A - Sqrt Bo(HDU 5752)

大意

定义 f(x) 的值为 x 开方后向下取整的结果。题给一个整数 n ,问满足 fy(n)=1 的最小的 y 是多少,如果这个 y 小于 5 则将它输出,否则输出TAT。

思路

当 n 值大到一定程度时,一定没法通过 5 次迭代得到 1 。我们将这个值算出来并将它赋值给upperBound。每当输入一个 n 个时候(注意要以字符串形式输入)我们就可以判断n与upperBound的关系,若比它大的话直接输出TAT(当心 n<1 的情况)。否则可以暴力迭代(不超过 5 次)算出结果。

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll upperBound = 4294836224LL;
ll a;
string s;
stringstream ss;

int main() {
while(cin >> s) {
if(s.size() > 10) {
puts("TAT");
continue;
}
ss.clear();
ss << s;
ss >> a;
if(a < 1 || a > upperBound) {
puts("TAT");
continue;
}
bool ok = false;
for(int i = 0; i <= 5; i++) {
if(a == 1) {
cout << i << endl;
ok = true;
break;
}
a = (ll)floor(sqrt(1.0 * a));
}
if(ok == false) {
puts("TAT");
}
}
return 0;
}


B - Permutation Bo(HDU 5753)

大意

题给一个序列 c ,另规定 1−n 的排列为 h 。定义 f(h)=∑ni=1ci[hi>hi−1andhi>hi+1] 。求 f(h) 的期望值。

思路

根据本题的数据规模来看,肯定无法枚举排列然后算期望了。一个可行的思维是算出序列 c 中每个元素对期望的贡献。

当考虑 c 的第 1 个元素 c1 时,我们只要考虑前两个元素的排列,当前两个元素呈现出 c1>c2 时 c1 的贡献就会被算进期望中。显然 c1>c2 的概率是 12 。

当考虑 c 的第 2 个元素 c2 时,我们只要考虑前三个元素的排列,当前三个排列呈现出 c1<c2,c3<c2 时, c2 的贡献就回被算进期望中。显然这种排列的概率是 13 。

第 3 到第 n−1 个元素的贡献与第 2 个元素的贡献相似。第 n 个元素的贡献与第 1 个元素的贡献相似。

于是我们枚举每个元素,将其贡献加入期望中即可。

代码

#include <bits/stdc++.h>
using namespace std;

int n, a;
double ans;

int main() {
while(~scanf("%d", &n)) {
ans = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a);
if(i == 1 || i == n) {
ans += 1.0 * a / 2;
}
else {
ans += 1.0 * a / 3;
}
}
if(n == 1) {
printf("%.5f\n", 1.0 * a);
}
else {
printf("%.5f\n", ans);
}
}
return 0;
}


C - Life Winner Bo(HDU 5754)

大意

有两个人在国际象棋棋盘上用国际象棋一个棋子进行博弈,这个棋子可能是王,王后,城堡和骑士。棋子从坐标 (1,1) 出发,两个人轮流让棋子按照国际象棋规则走一次,先让棋子到达 (N,M) 的人获胜。问先手和后手谁是赢家。

思路

根据国际象棋棋盘的特点,该游戏可以抽象成从两个石子堆(分别有 N−1,M−1 个石子)中拿石子,先将所有石子拿完的人获胜的博弈模型。根据四个棋子的走法不同,抽象模型中拿石子的方法也会有所不同。下面就分棋子进行讨论。

王:由于王可以在一次移动中横向,纵向或斜向移动一步,因此对应到拿石子模型中,当两堆石子的数量 N−1,M−1 都是偶数的时候当前状态是必败态(否则一定可以通过一次行动使得当前两堆石子的数量变成偶数)。

城堡:由于城堡可以在一次移动中横向或者纵向移动任意步,因此对应到拿石子的模型中,与 NimGame 模型是一样的。因此只有 N=M 的时候先手失败,其余情况下是先手获胜(先手总可以令对方面对两堆石子数目相同的情况,这种情况是必败态)。

骑士:由于骑士可以在一次移动中横移两步纵移一步或横移一步纵移两步,因此对应到拿石子模型中,相当于在 N−1,M−1 两堆石子中一堆拿一个,而另一堆拿两个。当 N−1+M−1 不是 3 的倍数的时候一定是平局。当它是 3 的倍数的时候,当 N=M 时一定是必败的,因为只要想脱离必败态(例如两堆石子分别 −1,−2 )就一定会被赶回必败态(例如两堆石子分别 −2,−1 )。若 N,M 不等且它们的差值为 1 时一定是必胜的,因为必定可以用一步把对手逼入必败态。

王后:由于王后可以在一次移动中横向,纵向或斜向移动任意步,因此对应到拿石子的模型中,与 WythoffGame 模型是一样的。因此可以用公式 ai=⌊i×(1+5√)/2⌋,bi=ai+i 检验当前局势 (N−1,M−1) 是否是必败局势。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1000;
char ch[] = {'G', 'D', 'B'};
int t, type, n, m, ans, G[maxn+5][maxn+5];

int king() {
return n % 2 == 1 && m % 2 == 1 ? -1 : 1;
}

int castle() {
return n == m ? -1 : 1;
}

int knight() {
if(n > m) {
swap(n, m);
}
if(n == m && (n - 4) % 3 == 0 && n >= 4) {
return -1;
}
if(n % 3 == 2 && m % 3 == 0 && m - n == 1) {
return 1;
}
return 0;
}

int queen() {
if(n > m) {
swap(n, m);
}
int a = n - 1, b = m - 1, k = m - n;
double tmp = (1.0 + sqrt((double)5.0)) / 2.0;
return (int)(k * tmp) == a ? -1 : 1;
}

int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &type, &n, &m);
if(type == 1) {
ans = king();
}
if(type == 2) {
ans = castle();
}
if(type == 3) {
ans = knight();
}
if(type == 4) {
ans = queen();
}
printf("%c\n", ch[ans+1]);
}
return 0;
}


J - Rower Bo(HDU 5761)

大意

主人公要驾船从平面直角坐标系中的 (0,a) 去到 (0,0) 。主人公的船相对水流的速度大小恒为 v1 ,方向始终朝向 (0,0) 。河流有一个垂直于 y 轴的速度 v2 。问主人公从出发到目的地要花多少时间。

思路

再思考无果的时候,不妨将问题转化一下:主人公从 (0,a) 出发,目的地以 v2 的速度向x轴正向移动,而水流是静止的。主人公始终以 v1 的速度朝向目的地移动,问主人公到达目的地的时间。

设T为主人公要花的时间,另外主人公在 t 时刻的速度与 x 轴方向的夹角为 θ(t) 。我们考虑主人公在水平和竖直方向上方向上的运动情况,对水平方向有

∫T0v1cosθ(t)dt=v2T

对竖直方向有

∫T0v1sinθ(t)dt=a

此时还有一个条件没有利用,那就是主人公的速度方向始终朝向终点,那么对主人公当前位置到目的地的方向有

∫T0(v1−v2cosθ(t))dt=a

最后联立这三个方程可得

T=v1av21+v22

于是原问题得解。

代码

#include <bits/stdc++.h>
using namespace std;

int a, v1, v2;

int main() {
while(~scanf("%d%d%d", &a, &v1, &v2)) {
if(a == 0) {
puts("0.00000");
}
else if(v1 <= v2) {
puts("Infinity");
}
else {
printf("%.5f\n", 1.0 * v1 * a / (v1 * v1 - v2 * v2));
}
}
return 0;
}


K - Teacher Bo(HDU 5762)

大意

题给 n 个点 (x,y) ,并约定 0≤x,y≤m 。问是否能从这些点钟找出一个四元组 (A,B,C,D) ,使得二元组 (A,B) 与二元组 (C,D) 的曼哈顿距离相等。(这两个二元组不能相同)

思路

虽然看上去不同的二元组会很多,但是根据鸽巢原理,当 n(n−1)2(不同的二元组数)>2√m(不同的曼哈顿距离数时) ,所求的四元组是一定能够被找到的,因此即使我们暴力枚举所有的二元组,复杂度也将是 min(n(n−1)2,2√m) 。为了立即发现重复,我们需要一个数组 vis , vis[x]=true 表示曼哈顿距离为x的二元组已经出现过了。在 m 不是很大的情况下,维护这样一个数组的空间也不会超出限制。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
bool ok;
int t, n, m, dx, dy, mht, x[maxn], y[maxn], vis[maxn];

int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &y[i]);
}
memset(vis, 0, sizeof(vis));
ok = false;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
dx = abs(x[i] - x[j]);
dy = abs(y[i] - y[j]);
mht = dx + dy;
if(vis[mht] == true) {
ok = true;
i = n + 1;
break;
}
vis[mht] = true;
}
}
puts(ok ? "YES" : "NO");
}
return 0;
}


(其它题目略)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息