AHU 周末练习 题解
=。= 这次比赛乱搞题比较多。。做的时候也比较烦躁。。感觉效率不是很高。
A: 水题,直接记录每个数字的位置然后输出就好了。
B: 题目看半天才明白,其实就是考一个三进制转化,很水。。
typedef long long LL;
const int maxn = 1024;
int numa[maxn], numc[maxn], numb[maxn];
int to3(LL num, int *str) {
int len = 0;
while(num) {
str[len++] = num % 3;
num /= 3;
}
return len;
}
int main() {
int a, b, c;
cin >> a >> c;
int lena = to3(a, numa);
int lenc = to3(c, numc);
int lenb = max(lena, lenc);
for(int i = 0; i < lenb; i++) {
numb[i] = (numc[i] - numa[i]) % 3 + 3;
numb[i] %= 3;
}
int ans = 0;
for(int i = lenb - 1; i >= 0; i--) ans = ans * 3 + numb[i];
cout << ans << endl;
return 0;
}
C: 注意特判一下全部元素都是1的情况,这时候只能交换到2,其他情况都是把最大的数换成1
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#include <stack>
#include <string>
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
const int maxn = 1e5 + 10;
int num[maxn];
int main() {
int n; cin >> n;
bool flag = false;
for(int i = 1; i <= n; i++) {
cin >> num[i];
if(num[i] != 1) flag = true;
}
if(!flag) {
for(int i = 1; i < n; i++) cout << "1 ";
cout << 2 << endl;
return 0;
}
num[0] = 1;
sort(num, num + n + 1);
for(int i = 0; i < n; i++) cout << num[i] << " ";
cout << endl;
return 0;
}
D: 题意是,给你八个点,问你能不能选出4个点使其构成正方形,剩下4个构成长方形。
先枚举所有点的全排列,然后只要无脑判断前4个点和后四个点就好了。
判断矩形只要判断相邻的边都垂直,正方形只要在这基础上保证四条边都相等就好。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
struct Point {
double x, y;
Point(double x, double y) :x(x), y(y) {}
Point() {};
};
Point operator - (Point a, Point b) {
return Point(a.x - b.x, a.y - b.y);
}
double dist(Point a) {
return sqrt(a.x*a.x + a.y*a.y);
}
double ag(Point a, Point b) {
return fabs(a.x*b.x + a.y*b.y);
}
const double eps = 1e-7;
Point p[10];
int id[10];
bool equ(double a, double b) {
if (fabs(a - b) < eps) return true;
return false;
}
bool isSquare(Point a1, Point a2, Point a3, Point a4) {
Point p1 = a1 - a2, p2 = a2 - a3, p3 = a3 - a4, p4 = a4 - a1;
double app = ag(p1, p2) + ag(p2, p3) + ag(p3, p4) + ag(p4, p1);
if (fabs(app) > eps) return false;
if (equ(dist(p1), dist(p2)) && equ(dist(p1), dist(p3)) && equ(dist(p1), dist(p4)))
return true;
return false;
}
bool isR(Point a1, Point a2, Point a3, Point a4) {
Point p1 = a1 - a2, p2 = a2 - a3, p3 = a3 - a4, p4 = a4 - a1;
double app = ag(p1, p2) + ag(p2, p3) + ag(p3, p4) + ag(p4, p1);
if (fabs(app) > eps) return false;
return true;
}
int main() {
for (int i = 1; i <= 8; i++) {
scanf("%lf%lf", &p[i].x, &p[i].y);
id[i] = i;
}
do {
bool r1 = isSquare(p[id[1]], p[id[2]], p[id[3]], p[id[4]]);
bool r2 = isR(p[id[5]], p[id[6]], p[id[7]], p[id[8]]);
if (r1 && r2) {
puts("YES");
printf("%d %d %d %d\n", id[1], id[2], id[3], id[4]);
printf("%d %d %d %d\n", id[5], id[6], id[7], id[8]);
return 0;
}
} while (next_permutation(id + 1, id + 1 + 8));
puts("NO");
return 0;
}
E: 题意是:给你一个01?序列,有两个人先后从序列中取数字,直到只剩两个数字的时候。先手的人想让剩下的数字尽可能小,后手的人想让数字尽可能的大。问?取01任意情况的时候,最后剩下的两位数的所有可能性。
考虑没有问号的情况,设cnt0为0的数量,cnt1为1的数量。
有如果cnt0 > cnt1 必为 00
cnt1 > cnt0 + 1 必为11
cnt1 == cnt0 或者 cnt1 == cnt0 + 1 的时候可能10也可能01,最后的值由序列的最后一个元素的决定。
那么加上问号之后 有cnt2表示问号的数量
如果可能达到cnt0+cntw>cnt1 00是有可能出现的
如果可能达到cnt1 + cntw > cnt0 + 1 11是有可能出现的
然后如果可能出现一种情况使得加上问号之后有cnt1==cnt0或者cnt1==cnt0+1,并且序列的最后一位不是问号,那么10或者01中的一个是可能出现的。
如果序列最后一个数是问号,那么分别讨论这个问号是0和1的情况,并且重新判断加上cnt2-1个问号之后,cnt1==cnt0,cnt1==cnt0+1是否可能达到。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#include <stack>
#include <string>
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
const int maxn = 2e5 + 10;
char buf[maxn];
void gao(char *str) {
int len = strlen(str);
int cnt0 = 0, cnt1 = 0, cntw = 0;
for(int i = 0; i < len; i++) {
if(str[i] == '0') cnt0++;
if(str[i] == '1') cnt1++;
if(str[i] == '?') cntw++;
}
if(cnt0 + cntw > cnt1) puts("00");
if(cnt0 + cntw + 1 >= cnt1 && cnt1 + cntw >= cnt0) {
if(str[len - 1] == '?') {
//假设最后一位填1
if(cnt0 + cntw >= cnt1 + 1 && cnt1 + cntw >= cnt0) puts("01");
//假设最后一位填0
if(cnt0 + cntw + 1 >= cnt1 && cnt1 + cntw - 1 >= cnt0 + 1) puts("10");
}
else if(str[len - 1] == '1') puts("01");
else puts("10");
}
if(cnt1 + cntw > cnt0 + 1) puts("11");
}
int main() {
while(scanf("%s", buf) != EOF) {
gao(buf);
}
return 0;
}
F 题意是战场上有n个横向或者纵向的沟壑,每a秒会有激光扫射战场b秒,人的移动速度是1,问你能否从起点到达终点。。保证b大于任意一个沟壑长度。
先预处理出来所有沟壑之间的距离,然后bfs一遍就好了,因为只要距离小于a,时间肯定是a。
bfs可以用优先队列加速一下~
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#include <stack>
#include <string>
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
const int maxn = 1e3 + 10;
const double eps = 1e-9;
int a, b, n;
double sx, sy, ex, ey;
double dist[maxn][maxn];
double posx1[maxn], posx2[maxn], posy1[maxn], posy2[maxn];
bool vis[maxn];
bool equ(double a, double b) {
return fabs(a - b) < eps;
}
struct Point {
double x, y;
Point (double x = 0, double y = 0):
x(x), y(y) {}
bool operator == (const Point &p) const {
return equ(p.x, x) && equ(p.y, y);
}
};
typedef Point Vector;
double sq(double x) {
return x * x;
}
Point operator - (Point a, Point b) {
return Point(a.x - b.x, a.y - b.y);
}
double getdist(Vector v) {
return sqrt(v.x * v.x + v.y * v.y);
}
double dot(Vector a, Vector b) {
return a.x * b.x + a.y * b.y;
}
double Cross(Vector a, Vector b) {
return a.x * b.y - a.y * b.x;
}
double getdist(Point p, Point a, Point b) {
if(a == b) return getdist(p - a);
Vector v1 = b - a, v2 = p - a, v3 = p - b;
if(dot(v1, v2) < 0) return getdist(v2);
else if(dot(v1, v3) > 0) return getdist(v3);
else return fabs(Cross(v1, v2)) / getdist(v1);
}
double getdist(int i, int j) {
//判断两条线段的距离
double x1 = posx1[i], x2 = posx2[i], y1 = posy1[i], y2 = posy2[i];
double x3 = posx1[j], x4 = posx2[j], y3 = posy1[j], y4 = posy2[j];
Point p1(x1, y1), p2(x2, y2), p3(x3, y3), p4(x4, y4);
double ret = min(getdist(p1, p3, p4), getdist(p2, p3, p4));
ret = min(ret, min(getdist(p3, p1, p2), getdist(p4, p1, p2)));
return ret;
}
struct Node {
int id; double cost;
Node(int id = 0, double cost = 0):
id(id), cost(cost) {}
bool operator < (const Node &x) const {
return cost > x.cost;
}
};
int main() {
scanf("%d%d", &a, &b);
scanf("%lf%lf%lf%lf", &sx, &sy, &ex, &ey);
scanf("%d", &n);
posx1[0] = posx2[0] = sx;
posy1[0] = posy2[0] = sy;
posx1[n + 1] = posx2[n + 1] = ex;
posy1[n + 1] = posy2[n + 1] = ey;
for(int i = 1; i <= n; i++) {
scanf("%lf%lf%lf%lf", &posx1[i], &posy1[i], &posx2[i], &posy2[i]);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
dist[i][j] = getdist(i, j);
}
}
//BFS
double pse = getdist(Point(sx, sy) - Point(ex, ey));
double ans = pse <= a ? pse : 1e12;
priority_queue<Node> q;
for(int i = 1; i <= n; i++) {
double ret = getdist(0, i);
if(ret <= a) {
q.push(Node(i, a + b));
}
}
while(!q.empty()) {
Node tt = q.top(); q.pop();
int now = tt.id;
double nowcost = tt.cost;
//printf("%d %.3f\n", now, nowcost);
if(getdist(now, n + 1) <= a) {
ans = min(ans, nowcost + getdist(now, n + 1));
}
for(int i = 1; i <= n; i++) if(dist[now][i] <= a + eps && !vis[i]) {
q.push(Node(i, nowcost + a + b));
vis[i] = true;
}
}
if(ans > 1e10) puts("-1");
else printf("%.10f\n", ans);
return 0;
}
G:水题,直接搞。
H: 题意是,给你一个序列,现在最多可以进行k次操作,每次操作可以让序列中任意一个元素取反,问你最大可能的长度为len的子序列和的绝对值是多少。
考虑使用简单的滑窗模型,维护一个长度为len的子序列,每次只要增删一个值并且统计处理就可以了。
现在需要的就是这样一个数据结构,
可以快速增加和删除元素,可以快速的求前k大的和。
其实用两个multiset就可以处理这样的问题。。或者用两个map模拟 multiset
也可以用树状数组套主席树。。不过没必要吧。。
具体看代码吧。。
至于处理绝对值,只要把所有数字取反再算一遍就好了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#include <stack>
#include <string>
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
multiset<int> st1, st;
int n, len, k, a[maxn];
LL sum = 0;
void insert(int num) {
if (k == 0) {
sum -= num; return;
}
if (st.size() == k) {
int pnum = *st.begin();
if (pnum < num) {
st.erase(st.begin());
sum = sum - 2 * pnum + num;
st.insert(num);
st1.insert(pnum);
}
else {
st1.insert(num);
sum -= num;
}
}
else {
sum += num; st.insert(num);
}
}
void erase(int num) {
if (k == 0) {
sum += num; return;
}
multiset<int>::iterator it = st.find(num);
if (it != st.end()) {
st.erase(it); sum -= num;
if (st1.size() != 0) {
it = st1.end(); it--;
insert((*it));
sum += *it;
st1.erase(it);
}
}
else {
it = st1.find(num);
if(it != st1.end()) st1.erase(it);
sum += num;
}
}
LL gao() {
LL ans = 0;
st.clear(); st1.clear(); sum = 0;
for (int i = 1; i <= len; i++) {
if (a[i] >= 0) sum += a[i];
else insert(-a[i]);
}
ans = sum;
for (int i = len + 1, j = 1; i <= n; i++, j++) {
if (a[j] < 0) erase(-a[j]);
else sum -= a[j];
if (a[i] < 0) insert(-a[i]);
else sum += a[i];
ans = max(ans, sum);
}
return ans;
}
int main() {
scanf("%d%d", &n, &len);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &k);
LL ans = gao();
for (int i = 1; i <= n; i++) a[i] = -a[i];
ans = max(ans, gao());
cout << ans << endl;
return 0;
}
I:伤心的一题。。。。可以直接暴力搞,复杂度不是很高,因为毕竟约数就不超过sqrt(n)个。我还花很久撸了一个KMP。。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1000005;
int f[maxn];
inline void getfail(char *str) {
int m = strlen(str);
f[0] = f[1] = 0;
for(int i = 2;i <= m;i++) {
int j = f[i - 1];
while(j && str[i - 1] != str[j]) j = f[j];
if(str[i - 1] == str[j]) f[i] = j + 1;
else f[i] = 0;
}
}
int gao(char *str) {
int n;
n = strlen(str);
getfail(str);
if(f[n] > 0 && n % (n - f[n]) == 0) {
return (n - f[n]);
}
else return n;
}
char str1[maxn], str2[maxn];
int main() {
int n,kase = 0;
while(scanf("%s%s",str1, str2) != EOF) {
int p1 = gao(str1), p2 = gao(str2);
int len1 = strlen(str1), len2 = strlen(str2);
if(strncmp(str1, str2, max(p1, p2)) == 0) {
if(p1 == p2) {
int ans = 0;
for(int i = p1; i <= min(len1, len2); i += p1) {
if(len1 % i == 0 && len2 % i == 0) {
ans++;
}
}
printf("%d\n", ans);
}
else puts("1");
}
else {
puts("0");
}
}
return 0;
}
J: DP水题,题目很长。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#include <stack>
#include <string>
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
struct Board {
int a, b;
Board(int a = 0, int b = 0) :
a(a), b(b) {}
bool operator == (const Board &x) const {
return a == x.a && b == x.b;
}
bool operator < (const Board &x) const {
if(x.a == a) return x.b < b;
return x.a < a;
}
};
const int mod = 1e9 + 7;
vector<Board> vec;
int f[4000][300][2], n, l;
int main() {
scanf("%d%d", &n, &l);
for(int i = 1; i <= n; i++) {
int a, b; scanf("%d%d", &a, &b);
if(a > b) swap(a, b);
vec.push_back(Board(a, b));
}
sort(vec.begin(), vec.end());
//vec.erase(unique(vec.begin(), vec.end()), vec.end());
int cnt_board = vec.size();
for(int i = 1; i <= l; i++) {
for(int j = 0; j < cnt_board; j++) {
if(i == vec[j].b) {
f[i][j][0] = (f[i][j][0] + 1) % mod;
}
if(i == vec[j].a && vec[j].a != vec[j].b) {
f[i][j][1] = (f[i][j][1] + 1) % mod;
}
else for(int k = 0; k < cnt_board; k++) if(j != k) {
if(i - vec[j].b > 0) {
if(vec[j].b == vec[k].a) f[i][j][0] = (f[i - vec[j].b][k][0] + f[i][j][0]) % mod;
if(vec[j].b == vec[k].b) f[i][j][0] = (f[i - vec[j].b][k][1] + f[i][j][0]) % mod;
}
if(i - vec[j].a > 0 && vec[j].a != vec[j].b) {
if(vec[j].a == vec[k].a) f[i][j][1] = (f[i - vec[j].a][k][0] + f[i][j][1]) % mod;
if(vec[j].a == vec[k].b) f[i][j][1] = (f[i - vec[j].a][k][1] + f[i][j][1]) % mod;
}
}
//printf("for length:%d, (%d,%d) ans is %d %d\n", i, vec[j].a, vec[j].b, f[i][j][0], f[i][j][1]);
}
}
int ans = 0;
for(int i = 0; i < cnt_board; i++) {
ans = (ans + f[l][i][0]) % mod;
ans = (ans + f[l][i][1]) % mod;
}
printf("%d\n", ans);
return 0;
}
K: 原本拉这题想考DP的,没想到直接找规律就可以过。。。
L:水题,注意相加之后不要溢出了。。

浙公网安备 33010602011771号