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

来源:互联网 发布:win10 软件 卸载 残留 编辑:程序博客网 时间:2024/06/05 19:44

题目链接


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 ,另规定 1n 的排列为 h 。定义 f(h)=ni=1ci[hi>hi1andhi>hi+1] 。求 f(h) 的期望值。

思路

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

  • 当考虑 c 的第 1 个元素 c1 时,我们只要考虑前两个元素的排列,当前两个元素呈现出 c1>c2c1 的贡献就会被算进期望中。显然 c1>c2 的概率是 12
  • 当考虑 c 的第 2 个元素 c2 时,我们只要考虑前三个元素的排列,当前三个排列呈现出 c1<c2,c3<c2 时, c2 的贡献就回被算进期望中。显然这种排列的概率是 13
  • 3 到第 n1 个元素的贡献与第 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) 的人获胜。问先手和后手谁是赢家。

思路

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

  • 王:由于王可以在一次移动中横向,纵向或斜向移动一步,因此对应到拿石子模型中,当两堆石子的数量 N1,M1 都是偶数的时候当前状态是必败态(否则一定可以通过一次行动使得当前两堆石子的数量变成偶数)。
  • 城堡:由于城堡可以在一次移动中横向或者纵向移动任意步,因此对应到拿石子的模型中,与 NimGame 模型是一样的。因此只有 N=M 的时候先手失败,其余情况下是先手获胜(先手总可以令对方面对两堆石子数目相同的情况,这种情况是必败态)。
  • 骑士:由于骑士可以在一次移动中横移两步纵移一步或横移一步纵移两步,因此对应到拿石子模型中,相当于在 N1,M1 两堆石子中一堆拿一个,而另一堆拿两个。当 N1+M1 不是 3 的倍数的时候一定是平局。当它是 3 的倍数的时候,当 N=M 时一定是必败的,因为只要想脱离必败态(例如两堆石子分别 12 )就一定会被赶回必败态(例如两堆石子分别 21 )。若 NM 不等且它们的差值为 1 时一定是必胜的,因为必定可以用一步把对手逼入必败态。
  • 王后:由于王后可以在一次移动中横向,纵向或斜向移动任意步,因此对应到拿石子的模型中,与 WythoffGame 模型是一样的。因此可以用公式 ai=i×1+5/2bi=ai+i 检验当前局势 (N1,M1) 是否是必败局势。

代码

#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(v1v2cosθ(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) ,并约定 0x,ym 。问是否能从这些点钟找出一个四元组 (A,B,C,D) ,使得二元组 (A,B) 与二元组 (C,D) 的曼哈顿距离相等。(这两个二元组不能相同)

思路

虽然看上去不同的二元组会很多,但是根据鸽巢原理,当 n(n1)2>2m ,所求的四元组是一定能够被找到的,因此即使我们暴力枚举所有的二元组,复杂度也将是 min(n(n1)2,2m) 。为了立即发现重复,我们需要一个数组 visvis[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;}

(其它题目略)

0 0
原创粉丝点击