HDU100题简要题解(2080~2089)

  • 2020 年 11 月 22 日
  • 筆記

//2089之前忘做了,周二C语言课上做,至于2086,写题解的时候突然发现之前的做法是错的,新的解法交上去CE,等周二再弄吧,其余题目暂时可以放心

Problem Description
这次xhd面临的问题是这样的:在一个平面内有两个点,求两个点分别和原点的连线的夹角的大小。
注:夹角的范围[0,180],两个点不会在圆心出现。
Input
输入数据的第一行是一个数据T,表示有T组数据。
每组数据有四个实数x1,y1,x2,y2分别表示两个点的坐标,这些实数的范围是[-10000,10000]。
Output
对于每组输入数据,输出夹角的大小精确到小数点后两位。
Sample Input
2
1 1 2 2
1 1 1 0
Sample Output
0.00
45.00

真就数学题呗,用向量去做,向量的积(ji)除以向量模的乘积(moji),再求其反函数,然后转换为角度
用到了acos()函数,它的返回值是弧度,要求角度需要 * 180/PI(PI就是π)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

const double PI = 3.1415967;
int t;
double x1, x2, z1, y2;
double ji, moji;

int main() {
  while (scanf("%d", &t) != EOF) {
    while (t--) {
      scanf("%lf%lf%lf%lf", &x1, &z1, &x2, &y2);
      moji = sqrt(x1 * x1 + z1 * z1) * sqrt(x2 * x2 + y2 * y2);
      ji = x1 * x2 + z1 * y2;
      printf("%.2lf\n", acos(ji / moji) / PI * 180.0);
    }
  }
  return 0;
} 

Problem Description
大家都知道,手机号是一个11位长的数字串,同时,作为学生,还可以申请加入校园网,如果加入成功,你将另外拥有一个短号。假设所有的短号都是是 6+手机号的后5位,比如号码为13512345678的手机,对应的短号就是645678。
现在,如果给你一个11位长的手机号码,你能找出对应的短号吗?
Input
输入数据的第一行是一个N(N <= 200),表示有N个数据,接下来的N行每一行为一个11位的手机号码。
Output
输出应包括N行,每行包括一个对应的短号,输出应与输入的顺序一致。
Sample Input
2
13512345678
13787654321
Sample Output
645678
654321

搞笑题

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n;
char c[12];

int main() {
  while (scanf("%d", &n) != EOF) {
    while (n--) {
      for (int i = 1; i <= 11; i++) cin >> c[i];
      cout << 6;
      for (int i = 7; i <= 11; i++) cout << c[i];
      cout << endl;
    }
  }
  return 0;
}

Problem Description
假设有x1个字母A, x2个字母B,….. x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,….. 字母Z的价值为26。那么,对于给定的字母,可以找到多少价值<=50的单词呢?单词的价值就是组成一个单词的所有字母的价值之和,比如,单词ACM的价值是1+3+14=18,单词HDU的价值是8+4+21=33。(组成的单词与排列顺序无关,比如ACM与CMA认为是同一个单词)。
Input
输入首先是一个整数N,代表测试实例的个数。
然后包括N行数据,每行包括26个<=20的整数x1,x2,…..x26.
Output
对于每个测试实例,请输出能找到的总价值<=50的单词数,每个实例的输出占一行。
Sample Input
2
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 2 6 2 10 2 2 5 6 1 0 2 7 0 2 2 7 5 10 6 10 2 10 6 1 9
Sample Output
7
379297

多重背包,把26个字母看做26种物品,容量50

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n, num[101], val[101];
int dp[101][101];

int main() {
  while (scanf("%d", &n) != EOF) {
    while (n--) {
    memset(dp, 0, sizeof(dp));
      for (int i = 1; i <= 26; i++) {
  	val[i] = i;
  	scanf("%d", &num[i]);
      }
      dp[0][0] = 1;
      for (int i = 1; i <= 26; i++)
  	for (int j = 0; j <= 50; j++)
  	  for (int k = 0; k <= num[i]; k++) 
  	    if (val[i] * k <= j) 
  	      dp[i][j] += dp[i - 1][j - val[i] * k];
  	    else break;
      int ans = 0;
      for (int i = 1; i <= 50; i++) 
  	ans += dp[26][i];
      printf("%d\n", ans);
    }
  }
  return 0;
} 

Problem Description
寒假的时候,ACBOY要去拜访很多朋友,恰巧他所有朋友的家都处在坐标平面的X轴上。ACBOY可以任意选择一个朋友的家开始访问,但是每次访问后他都必须回到出发点,然后才能去访问下一个朋友。
比如有4个朋友,对应的X轴坐标分别为1, 2, 3, 4。当ACBOY选择坐标为2的点做为出发点时,则他最终需要的时间为 |1-2|+|2-2|+|3-2|+|4-2| = 4。
现在给出N个朋友的坐标,那么ACBOY应该怎么走才会花费时间最少呢?
Input
输入首先是一个正整数M,表示M个测试实例。每个实例的输入有2行,首先是一个正整数N(N <= 500),表示有N个朋友,下一行是N个正整数,表示具体的坐标(所有数据均<=10000).
Output
对于每一个测试实例,请输出访问完所有朋友所花的最少时间,每个实例的输出占一行。
Sample Input
2
2
2 4
3
2 4 6
Sample Output
2
4

二话不说,直接暴力

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define INF 1e7 + 1
using namespace std;

int m, n, a[501];

int main() {
  while (scanf("%d", &m) != EOF) {
    while (m--) {
      scanf("%d", &n);
      for (int i = 1; i <= n; i++) 
  	scanf("%d", &a[i]);
      int ans = INF;
      for (int i = 1; i <= n; i++) {
  	int sum = 0;
  	for (int j = 1; j <= n; j++)
  	  sum += abs(a[i] - a[j]);
  	ans = min(ans, sum);
      }
      printf("%d\n", ans);
    }
  }
  return 0;
}

Problem Description
在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

已经告诉你了,这是个DP的题目,你能AC吗?
Input
输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。
Output
对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
Sample Input
1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30

既然题目都说了DP那就DP呗,从下往上推,最后输出dp[1][1]即可

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int c, n;
int a[101][101], dp[101][101];

int main() {
  while (scanf("%d", &c) != EOF) {
    while (c--) {
      scanf("%d", &n);
      for (int i = 1; i <= n; i++)
  	for (int j = 1; j <= i; j++)
  	  scanf("%d", &a[i][j]);
      for (int i = n; i >= 1; i--)
  	for (int j = 1; j <= i; j++) 
  	  dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + a[i][j];
      printf("%d\n", dp[1][1]);
    }
  }
  return 0;
} 

Problem Description
某核反应堆有两类事件发生:
高能质点碰击核子时,质点被吸收,放出3个高能质点和1个低能质点;
低能质点碰击核子时,质点被吸收,放出2个高能质点和1个低能质点。
假定开始的时候(0微秒)只有一个高能质点射入核反应堆,每一微秒引起一个事件发生(对于一个事件,当前存在的所有质点都会撞击核子),试确定n微秒时高能质点和低能质点的数目。
Input
输入含有一些整数n(0≤n≤33),以微秒为单位,若n为-1表示处理结束。
Output
分别输出n微秒时刻高能质点和低能质点的数量,高能质点与低能质点数量之间以逗号空格分隔。每个输出占一行。
Sample Input
5 2
-1
Sample Output
571, 209
11, 4
提示
可以使用long long int对付GNU C++,使用__int64对付VC6

算算数洒洒水,高能质点的数量就是前一微秒的高能质点 * 3 + 低能质点 * 2,低能质点的数量就是前一微秒的高能质点 + 低能质点
然后注意初始化a[0] = 1, b[0] = 0即可

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n;
long long a[34], b[34];

int main() {
  while (scanf("%d", &n) != EOF) {
    if (n == -1) break;
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    a[0] = 1;
    b[0] = 0;
    for (int i = 1; i <= n; i++) {
      a[i] = a[i - 1] * 3 + b[i - 1] * 2;
      b[i] = a[i - 1] + b[i - 1];
    }
    printf("%lld, %lld\n", a[n], b[n]);
  }
  return 0;
}

Problem Description
有如下方程:Ai = (Ai-1 + Ai+1)/2 – Ci (i = 1, 2, 3, …. n).
若给出A0, An+1, 和 C1, C2, …..Cn.
请编程计算A1 = ?
Input
输入包括多个测试实例。
对于每个实例,首先是一个正整数n,(n <= 3000); 然后是2个数a0, an+1.接下来的n行每行有一个数ci(i = 1, ….n);输入以文件结束符结束。
Output
对于每个测试实例,用一行输出所求得的a1(保留2位小数).
Sample Input
1
50.00
25.00
10.00
2
50.00
25.00
10.00
20.00
Sample Output
27.50
15.00

这,是一道数学题,就由题目给的公式去推

因为:
   Ai = (Ai - 1 + Ai + 1) / 2 - Ci
可得:
   A1+A1 =  A0+A2 - 2(C1) 
   A1+A2 =  A0+A3 - 2(C1+C2)
   A1+A3 =  A0+A4 - 2(C1+C2+C3)
   ...
   A1+An = A0+An+1 - 2(C1+C2+...+Cn)
左右均求和可得:
   (n+1)A1+(A2+A3+...+An) = nA0 +(A2+A3+...+An) + An+1 - 2(nC1+(n-1)C2+...+2Cn-1+Cn)
化简得:
   A1 = [nA0 + An+1 - 2(nC1+(n-1)C2+...+2Cn-1+Cn)]/(n+1)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n;
double cnt, a0, an1, a1, c[3001];

int main() {
  while (scanf("%d", &n) != EOF) {
    scanf("%lf%lf", &a0, &an1);
    cnt = 0;
    for (int i = 1; i <= n; i++)
      scanf("%lf", &c[i]);
    for (int i = 1; i <= n; i++)
      cnt += 2 * i * c[n - i + 1];
    a1 = (n * a0 + an1 - cnt) / (n + 1);
    printf("%.2lf\n", a1);
  }
  return 0;
}

Problem Description
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
‘#’(为了排版加了引号)
Sample Output
0
3

KMP!!不会,就普普通通的做法然后水了过去…但是!!!

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

char a[1005], b[1005];

int main() {
  while (scanf("%s", a) != EOF) {
    if (a[0] == '#') break;
    scanf("%s", b);
    int len1 = strlen(a);
    int len2 = strlen(b);
    int cnt = 0;
    int ans = 0;
    for (int i = 0; i < len1; i++) {
      if (a[i] == b[cnt]) {
        cnt++;
  	if (cnt == len2) {
  	 ans++;
  	 cnt = 0;
  	}
      } else {
  	cnt = 0;
      }
    }
    printf("%d\n", ans);
  }
  return 0;
}

但是!!!我在写题解的时候猛然发现,这个做法肯定是不正确的!例如说输入样例abababc ababc,上述做法的答案是0,但实际上应该是1
所以,还是乖乖kmp吧,交上去CE了不知道为啥,但是本地是没事的…不知道为啥

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

char a[1005], b[1005];
int len1, len2, next[1005];

void getnext() {
  int cnt = 0;
  int k = -1;
  next[0] = -1;
  while (cnt < len2) {
    if(k == -1 || b[cnt] == b[k]) {
      cnt++;
      k++;
      next[cnt]=k;
    } else k = next[k];
  }
}

int kmp() {
  getnext();
  int i = 0, j = 0;
  int cnt=0;
  while (i < len1) {
    if (j == -1 || a[i] == b[j]) {
      i++;
      j++;
    } else j = next[j];
    if (j == len2) {
      cnt++;
      j = 0; 
    }
  }
  return cnt;
}
int main() {
  while (scanf("%s", a) != EOF) {
    if (a[0] == '#') break;
    scanf("%s", b);
    len1 = strlen(a);
    len2 = strlen(b);    
    printf("%d\n", kmp());
  }
  return 0;
}

Problem Description
Little Bob likes playing with his box of bricks. He puts the bricks one upon another and builds stacks of different height. “Look, I’ve built a wall!”, he tells his older sister Alice. “Nah, you should make all stacks the same height. Then you would have a real wall.”, she retorts. After a little consideration, Bob sees that she is right. So he sets out to rearrange the bricks, one by one, such that all stacks are the same height afterwards. But since Bob is lazy he wants to do this with the minimum number of bricks moved. Can you help?

Input
The input consists of several data sets. Each set begins with a line containing the number n of stacks Bob has built. The next line contains n numbers, the heights hi of the n stacks. You may assume 1≤n≤50 and 1≤hi≤100.
The total number of bricks will be divisible by the number of stacks. Thus, it is always possible to rearrange the bricks such that all stacks have the same height.
The input is terminated by a set starting with n = 0. This set should not be processed.
Output
For each set, print the minimum number of bricks that have to be moved in order to make all the stacks the same height.
Output a blank line between each set.
Sample Input
6
5 2 4 1 7 5
0
Sample Output
5

比平均高度矮的高的差值都加起来除以2就好了

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n, h[51], flag;

int main() {
  while (scanf("%d", &n) != EOF) {
    if (n == 0) break;
    if (flag != 0) printf("\n");
    flag = 1;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
      scanf("%d", &h[i]);
      sum += h[i];
    }
    sum /= n;
    int ans = 0;
    for (int i = 1; i <= n; i++) 
      ans += abs(sum - h[i]);
    printf("%d\n", ans / 2);
  }
  return 0;
}