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:水题,注意相加之后不要溢出了。。

 

 

posted @ 2014-11-18 18:53  acm_roll  阅读(225)  评论(0编辑  收藏  举报