今天无聊地蛋疼,所以在牛客网随便找了套题做做,随手写了下解题报告,套题链接:点这里
总体来说这套题就考了下贪心和模拟,最后一题考了一个经典的动态规划,其实也有别的方法可以搞,这题对于大家练手还是不错的.
第一题:
题目:
现在有一张半径为r的圆桌,其中心位于 (x,y) ,现在他想把圆桌的中心移到 (x1,y1) 。每次移动一步,都必须在圆桌边缘固定一个点然后将圆桌绕这个点旋转。问最少需要移动几步。
解析:
沿两圆心的直线移动是最短的,因此,固定的点固定在直线上,每次移动 2r 距离,快到目标点时,我们就不要把点固定在直线上了,以当前点和目标点的连线作为等腰三角形的底,两条半径做腰,这样三角形的顶点就是固定点,移动就是了。
代码:
#include <bits/stdc++.h>
using namespace std;
double dis(int x, int y, int x1, int y1)
{
return sqrt(1.0 * (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y));
}
int main()
{
int r, x, y, x1, y1;
while (cin >> r >> x >> y >> x1 >> y1) {
double ans = dis(x, y, x1, y1) / (2 * r);
cout << ceil(ans) << endl;
}
return 0;
}
第二题:
题目:
给定一个递增序列, a1<a2<...<an 。定义这个序列的最大间隔为 d=max{ai+1−ai}(1≤i<n) ,现在要从 a2,a3...,an−1 中删除一个元素。问剩余序列的最大间隔最小是多少?
解析:
先预处理下原序列每两个元素的间隔,然后就开始删元素,这里有个技巧,我们在第一遍预处理的时候就求出一个间隔的最大值,然后删元素的时候,如果没删掉这个最大值,那么就把新得到的一个间隔(前后两个间隔相加)和这个最大值比较,如果删掉了这个最大值,那么新得到的间隔一定比这个最大值大,即 ans=min(ans,max(maxInter,inter[i]+inter[i+1])) .
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
while (cin >> n) {
int *a = new int[n], *inter = new int[n];
cin >> a[0];
int maxInter = 0;
for (int i = 1; i < n; i++)
cin >> a[i], inter[i] = a[i] - a[i - 1], maxInter = max(maxInter, inter[i]);
if (n == 1) {
cout << 0 << endl;
break;
}
if (n == 2) {
cout << maxInter << endl;
break;
}
int ans = 0x3f3f3f3f;
for (int i = 1; i < n - 1; i++)
ans = min(ans, max(maxInter, inter[i] + inter[i + 1]));
cout << ans << endl;
}
return 0;
}
第三题:
题目:
A和B是好友,他们经常在空闲时间聊天,A的空闲时间为 [a1,b1],[a2,b2]...[ap,bp] 。B的空闲时间是 [c1+t,d1+t]...[cq+t,dq+t] ,这里t为B的起床时间。这些时间包括了边界点。B的起床时间为 [l,r] 的一个时刻。若一个起床时间能使两人在某一时刻聊天,那么这个时间就是合适的,问有多少个合适的起床时间?
解析:
题目表达不清,这是我改了下的题意,直接暴力就好了,求区间交,应该都会吧.
代码:
#include <bits/stdc++.h>
using namespace std;
typedef struct tagInterval Interval;
typedef Interval *pInterval;
struct tagInterval {
int left, right;
};
int main()
{
int p, q, l, r;
while (cin >> p >> q >> l >> r) {
pInterval A = new Interval[p], B = new Interval[q];
for (int i = 0; i < p; i++)
cin >> A[i].left >> A[i].right;
for (int i = 0; i < q; i++)
cin >> B[i].left >> B[i].right;
int ans = 0;
for (int i = l; i <= r; i++) {
for (int x = 0; x < p; x++) {
for (int y = 0; y < q; y++) {
if ((A[x].left <= B[y].left + i && B[y].left + i <= A[x].right) ||
(A[x].left <= B[y].right + i && B[y].right + i <= A[x].right)) {
++ans;
goto F;
}
}
}
F:;
}
cout << ans << endl;
delete[] A;
delete[] B;
}
return 0;
}
第四题:
题目:
有一个投篮游戏。球场有p个篮筐,编号为 0,1...,p−1 。每个篮筐下有个袋子,每个袋子最多装一个篮球。有n个篮球,每个球编号 xi 。规则是将数字为 xi 的篮球投到 xi 除p的余数为编号的袋里。若袋里已有篮球则球弹出游戏结束输出i,否则重复至所有球都投完。输出-1。问游戏最终的输出是什么?
解析:
这题就是考一下在线处理……
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int p, n;
while (cin >> p >> n) {
bool *ba = new bool[p];
memset(ba, false, sizeof(bool) * p);
bool isOver = false;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
if (isOver)
continue;
if (ba[x % p]) {
cout << i << endl;
isOver = true;
} else
ba[x % p] = true;
}
if (!isOver)
cout << -1 << endl;
delete[] ba;
}
return 0;
}
第五题:
题目:
给定一个字符串,问是否能通过添加一个字母将其变为回文串。
解析:
求原串和反串的最长公共子序列,如果最长公共子序列的长度是字符串的长度或者是字符串的长度减一,那么就是可行的;
看到有人是这么做的:既然可以添加一个字符变成回文串,那么就可以删除一个字符变成回文串,因此,枚举删除每个字符,再判每个字符串是不是回文串,这也是可以的,不过时间复杂度有点高.
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
string str;
while (cin >> str) {
string rev = str;
reverse(rev.begin(), rev.end());
int dp[(const int)(str.size() + 5)][(const int)(str.size() + 5)];
memset(dp, 0, sizeof(dp));
// dp[0][0] = 1;
for (int i = 0; i < str.size(); i++) {
for (int j = 0; j < str.size(); j++) {
if (str[i] == rev[j])
dp[i + 1][j + 1] = dp[i][j] + 1;
else
dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
}
}
// cout << dp[str.size()][str.size()] << endl;
puts(dp[str.size()][str.size()] == str.size() || dp[str.size()][str.size()] == str.size() - 1 ? "YES" : "NO");
}
return 0;
}