gougou40 (1)
开始做狗狗40题,不知道在毕业前能不能做完。
第一题The Willy Memorial Program就虐死我了,恶心的模拟……
数据范围超小,于是开始乱搞……我的思路是用水将能填满的水管都填满,填满的步骤是根据水位来进行的(每升高单位水位算一个步骤),于是每个步骤可以分为以下几个小步骤:1~判断当前共有几个水管是一起升高水位的(程序中用n表示),那个该步骤就加入n个单位水位,使得这些水管中每个水管的水位上升一单位;2~判断当前有无可能达到目标水位线;3~若达到目标水位线,判断是否已溢出(虽然在程序中认为水管有盖,但是题目中水管是没有盖的,所以要加入这个判断,这个判断比较简单,写个循环判断水位线是否达到了已存在的水管中y值在最低——或者说y值在最大——位置的水管盖子),若没有则进入下一步。最后,若所有水管已填满但还没有达到指定水位线则算溢出情况。
因为实际目标比实际目标水位线稍微高一点,如果到达水位线后直接进行判断有点繁琐(考虑到连接其他的水管,题目中也已提到),所以我用这一步的下一步进行判断,下一步若淹过目标水位线,则前一步就是答案,只需要这一步的总体积减去这一步所用的体积(程序中的v-n)即为答案。
当然,之前需要先预处理一下水管之间的连接情况(储存在程序中的pc)。在判断连接水管时用vector储存(我是用vector来实现BFS的)。
注意:有数据是指定水位线并不在水管内部(尤其是在水管下方,已经超出水管范围的位置,这里我WA了不知道多多少次才发现)。
参考程序如下。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <ctime> #include <fstream> using namespace std; int t, p, px[20], py[20], height[20], l, lx, ly, length, target, level, mat[20], w[20]; vector<int> myvector; struct { int n, to[50], pos[50]; }pc[20]; int present(int *mat) { int n = 0, wlevel = 0; for(int i = 0; i < myvector.size(); i++) { int m = myvector[i]; for(int j = 0; j < pc[m].n; j++) { if(py[m] + height[m] - w[m] <= pc[m].pos[j] && find(myvector.begin(), myvector.end(), pc[m].to[j]) == myvector.end()) myvector.push_back(pc[m].to[j]); } } int sz = myvector.size(); for(int i = 0; i < sz; i++) { int m = myvector[i]; if(w[m] < height[m] && py[m] + height[m] - w[m] > wlevel) { n = 0; mat[n++] = m; wlevel = py[m] + height[m] - w[m]; } else if(w[m] < height[m] && py[m] + height[m] - w[m] == wlevel) mat[n++] = m; } return n; } bool boiled() { if(w[target] + level > py[target] + height[target]) return 1; return 0; } bool filled() { int mx = 0; for(int i = 0; i < p; i++) if(w[i]) mx = max(mx, py[i]); if(level <= mx) return 1; return 0; } int main() { cin >> t; while(t--) { cin >> p; for(int i = 0; i < p; i++) cin >> px[i] >> py[i] >> height[i]; memset(pc, 0, sizeof(pc)); cin >> l; for(int i = 0; i < l; i++) { cin >> lx >> ly >> length; for(int j = 0; j < p; j++) for(int k = 0; k < p; k++) if((px[j] + 1 == lx && px[k] == lx + length) || (px[j] == lx + length && px[k] + 1== lx)) { pc[j].to[pc[j].n] = k; pc[j].pos[pc[j].n] = ly; pc[j].n++; } } cin >> target >> level; target--; memset(w, 0, sizeof(w)); myvector.clear(); myvector.push_back(0); int v = 0, n = 0; bool b = 1; while(v <= 400) { n = present(mat); if(!n) { b = 0; break; } v += n; for(int i = 0; i < n; i++) w[mat[i]]++; if(boiled()) break; } if(b && level <= py[target] + height[target] && !filled()) cout << v - n << endl; else cout << "No Solution" << endl; } return 0; }
第二题Farmland搞定。
我的思路是去找逆时针旋转,定下某个顶点时,角度改变最大的那个点(第三个点),这里共涉及两条边,还未超过该点时的一条边(该点与该点之前的点组成的边),以及超过该点时的一条边(该点与该点之后的点组成的边),以该点位旋转点时,前一条边经过最大的角度(逆时针旋转)达到另一条边。按照这个思路,当完成点与点相邻情况的储存后(邻接表),定下前两个点(该点前一个点和该点)就可以确定第三个点(该点之后的点),以上“该点”在程序中设为o点(因为两条边是通过该点旋转的,有中心的意思),接着设该o点之前点为m点,那么可以唯一确定第三个点设为n点,于是用一个next函数储存,即next[m][o]=n(m与o不相邻时,next值为-1)。
实际上以上的程序还有一个问题,这是在我一开始没有想到的,也为此WA了很多次,就是这样用next函数确定多边形,有两种情况:一种是题目所要求的多边形(内部没有杂点杂边,不然在题设图是connected的情况下,选择点和边时会选中里面的点和边,原因就是选择这些点和边会比外面的点和边旋转角度更大),此时,这个多边形是正方向的(对于内部来说),且是逆时针的;另一种则是当整个图的外部没有杂点和杂边的时候,有可能会产生这个图的最外部的多边形(实际上,若把无穷大点也作为一个有意义的点的时候,这种多边形也是满足题意的,因为这个多边形的方向对于外部区域来说是正方向的,虽然它本身是顺时针的,此时这个多边形的内部就是整个图的外部,这种思想在复变函数中很常见,比如求一个复变函数的回路积分的时候,当这个复变函数只有有限个孤立奇点,且孤立奇点都在回路内部,那么就可以用无穷大点——也是一个孤立奇点——的留数来计算,此时的正方向就是顺时针而非逆时针)。解决的方法是多一个多边形是逆时针还是顺时针的判断:在平面中任取一点(一般为原点),按照点的顺序,求每两个相邻的点的cross product(严格地来说,是这两点与原点组成的两个向量的cross product),想加后求出来的是这个多边形的面积(的两倍),当然,这个面积是带方向的,正为逆时针,负为顺时针,证明略麻烦,以后有时间了再把严格的证明放上来。
具体在实施,判断旋转角度的时候我用的是cross product和inner product共同判断的方法(前者主要判断正负来决定位置,后者在决定了位置后决定其角度的大小,注意前者因为只需要正负所以不需要单位化,但后者需要用大小所以需要单位化处理,这里我也WA了几次)。
另外,求出的答案需要除以size,因为从满足条件的多边形上的每个点出发都可以得到同一多边形。
具体的代码如下。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <ctime> #include <fstream> using namespace std; double dist(int x, int y) { return sqrt(double(x * x + y * y)); } double InnerProduct(int xa, int ya, int xb, int yb) { double d = dist(xb, yb); return xa * (xb / d) + ya * (yb / d); } int CrossProduct(int xa, int ya, int xb, int yb) { return xa * yb - ya * xb; } int main() { int M, N, v[200], rv[200], x[200], y[200], d[200], sd, ad[200][200], next[200][200], sz, mat[201], sum; vector<int> myvector; cin >> M; while(M--) { memset(rv, -1, sizeof(rv)); memset(next, -1, sizeof(next)); sd = sum = 0; cin >> N; for(int i = 0; i < N; i++) { cin >> v[i]; rv[v[i]] = i; cin >> x[v[i]] >> y[v[i]] >> d[v[i]]; sd += d[v[i]]; for(int j = 0; j < d[v[i]]; j++) cin >> ad[v[i]][j]; } for(int i = 0; i < N; i++) { int o = v[i]; for(int j = 0; j < d[o]; j++) { int m = ad[o][j], index = ad[o][0]; for(int k = 1; k < d[o]; k++) { if(m == ad[o][k]) continue; if(m == index) { index = ad[o][k]; continue; } int n = ad[o][k]; int OMcpON = CrossProduct(x[m] - x[o], y[m] - y[o], x[n] - x[o], y[n] - y[o]); int OMcpOI = CrossProduct(x[m] - x[o], y[m] - y[o], x[index] - x[o], y[index] - y[o]); if(OMcpON) { if(OMcpON > 0 && OMcpOI > 0) { double OMipON = InnerProduct(x[m] - x[o], y[m] - y[o], x[n] - x[o], y[n] - y[o]); double OMipOI = InnerProduct(x[m] - x[o], y[m] - y[o], x[index] - x[o], y[index] - y[o]); if(OMipON < OMipOI) index = n; } if(OMcpON < 0) { if(OMcpOI >= 0) index = n; else { double OMipON = InnerProduct(x[m] - x[o], y[m] - y[o], x[n] - x[o], y[n] - y[o]); double OMipOI = InnerProduct(x[m] - x[o], y[m] - y[o], x[index] - x[o], y[index] - y[o]); if(OMipON > OMipOI) index = n; } } } else if(OMcpOI > 0) index = n; } next[m][o] = index; } } /*check the next function for(int i = 0; i < 200; i++) for(int j = 0; j < 200; j++) if(next[i][j] != -1) cout << i << ' ' << j << ' ' << next[i][j] << endl; */ cin >> sz; for(int i = 0; i < N; i++) { int o = v[i]; for(int j = 0; j < d[o]; j++) { int tmpsz = sz - 2; myvector.clear(); myvector.push_back(o); int pre = o, no = ad[o][j]; if(find(myvector.begin(), myvector.end(), no) == myvector.end()) myvector.push_back(no); int post = no; while(tmpsz--) { int tmp = next[pre][post]; if(tmp == -1) break; if(find(myvector.begin(), myvector.end(), tmp) == myvector.end()) myvector.push_back(tmp); pre = post; post = tmp; } if(myvector.size() == sz && next[pre][post] == o && next[post][o] == no) { tmpsz = 0; for(vector<int>::iterator it = myvector.begin(); it != myvector.end(); it++) mat[tmpsz++] = *it; mat[tmpsz] = *myvector.begin(); int area = 0; for(int k = 0; k < tmpsz; k++) { //cout << mat[k] << ' ' << mat[k + 1] << endl; area += CrossProduct(x[mat[k]], y[mat[k]], x[mat[k + 1]], y[mat[k + 1]]); } //cout << area << endl; if(area > 0) sum++; } } } //cout << sum << endl; cout << sum / sz << endl; } return 0; }
第三题Transmitters搞定!
1A搞定,这题原来我两年前做过,当时用的还是C,估计是为了凑齐当时选入集训队的99分……这题比起上两题来说不知道要人性化多少,用一个cross product判断以r为中心的两个点(有顺序)的逆时针角度是否超过pi就行,复杂度O(n^2)即可过(n=150的数据量实在是很小,0msAC)。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <ctime> #include <fstream> using namespace std; int dist(int rx, int ry, int x, int y) { return (x - rx) * (x - rx) + (y - ry) * (y - ry); } int CrossProduct(int xi, int yi, int xj, int yj) { return xi * yj - yi * xj; } int main() { int rx, ry; double r; while(cin >> rx >> ry >> r) { if(r < 0) break; r = r * r; int N, x[150], y[150], k = 0; cin >> N; for(int i = 0, _x, _y; i < N; i++) { cin >> _x >> _y; if(dist(rx, ry, _x, _y) <= r) { x[k] = _x; y[k] = _y; k++; } } int mx = 0; for(int i = 0; i < k; i++) { int cnt = 0; for(int j = 0; j < k; j++) if(CrossProduct(x[i] - rx, y[i] - ry, x[j] - rx, y[j] - ry) >= 0) cnt++; mx = max(mx, cnt); } cout << mx << endl; } return 0; }
结束了答辩(好吧,我貌似还要二辩),继续gougou40的节奏。
Split Windows是一道巨恶心的模拟题(虽然目测后面还有更多更恶心的模拟题),本来打算用链表写树,后来觉得没这个必要,还是用数组写了。
我的思路用了两次递归,第一次是计算总window的width&height,第二次是根据父节点的width&height推子节点的width&height,同时画出此split时window的形状。一开始觉得好难画,后来想了一下,递归的时候把某时刻的LRUD(左右上下,也就是子window的框架)全部记录,并递归下去的话,还是比较容易画的。具体画法是:首先画出window的轮廓(初始化),每分割一次就画分割线(注意当'-'和'|'遇到时要变成'*'),最后递归到字母时,直接在左上角将本来的字符用字母覆盖即可。
数据应该不是太强,而且样例也给的也应该是数据里面比较强的了,所以只要能写出一般就不会WA(不过因为一开始window的初始化错误,我还是WA了3次,太弱了……)。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <ctime> #include <fstream> using namespace std; const long double pi = 3.1415926535898; #define maxw 1024 #define maxn 128 char window[maxw][maxw]; struct T { char label; int parent, left, right, width, height; }Tree[maxn]; void calculate(int root) { if(isupper(Tree[root].label)) return; int left = Tree[root].left, right = Tree[root].right; calculate(left); calculate(right); if(Tree[root].label == '-') { Tree[root].width = max(Tree[left].width, Tree[right].width); Tree[root].height = Tree[left].height + Tree[right].height; } if(Tree[root].label == '|') { Tree[root].height = max(Tree[left].height, Tree[right].height); Tree[root].width = Tree[left].width + Tree[right].width; } } void stretch(int root, int L, int R, int U, int D) { if(isupper(Tree[root].label)) { window[U][L] = Tree[root].label; return; } int left = Tree[root].left, right = Tree[root].right; if(Tree[root].label == '-') { Tree[left].width = Tree[right].width = Tree[root].width; int sheight = Tree[left].height + Tree[right].height; Tree[left].height = Tree[root].height * Tree[left].height / sheight + (Tree[root].height * Tree[left].height % sheight != 0); Tree[right].height = Tree[root].height - Tree[left].height; int h = U + Tree[left].height; for(int i = L; i <= R; i++) if(window[h][i] == ' ') window[h][i] = '-'; else if(window[h][i] == '|') window[h][i] = '*'; } if(Tree[root].label == '|') { int swidth = Tree[left].width + Tree[right].width; Tree[left].width = Tree[root].width * Tree[left].width / swidth + (Tree[root].width * Tree[left].width % swidth != 0); Tree[right].width = Tree[root].width - Tree[left].width; Tree[left].height = Tree[right].height = Tree[root].height; int w = L + Tree[left].width; for(int i = U; i <= D; i++) if(window[i][w] == ' ') window[i][w] = '|'; else if(window[i][w] == '-') window[i][w] = '*'; } stretch(left, L, L + Tree[left].width, U, U + Tree[left].height); stretch(right, R - Tree[right].width, R, D - Tree[right].height, D); } int main() { int N, n = 1; cin >> N; while(N--) { string str; cin >> str; int sz = str.size(), parent = 0; Tree[0].parent = Tree[0].left = Tree[0].right = -1; if(isupper(Tree[0].label = str[0])) Tree[0].width = Tree[0].height = 2; else Tree[0].width = Tree[0].height = 0; for(int i = 1; i < sz; i++) { Tree[i].parent = parent; Tree[i].left = Tree[i].right = -1; if(Tree[parent].left == -1) Tree[parent].left = i; else Tree[parent].right = i; if(isupper(Tree[i].label = str[i])) { while(parent >= 0 && Tree[parent].right >= 0) parent = Tree[parent].parent; Tree[i].width = Tree[i].height = 2; } else { parent = i; Tree[i].width = Tree[i].height = 0; } } /*check the Tree for(int i = 0; i < sz; i++) cout << Tree[i].label << ' ' << Tree[i].parent << ' ' << Tree[i].left << ' ' << Tree[i].right << endl; */ calculate(0); //cout << Tree[0].width << ' ' << Tree[0].height << endl;//check the width and height of the window for(int i = 0; i <= Tree[0].height; i++) { for(int j = 0; j <= Tree[0].width; j++) if((i == 0 || i == Tree[0].height) && (j == 0 || j == Tree[0].width)) window[i][j] = '*'; else if(i == 0 || i == Tree[0].height) window[i][j] = '-'; else if(j == 0 || j == Tree[0].width) window[i][j] = '|'; else window[i][j] = ' ';//initialize the window window[i][Tree[0].width + 1] = 0; } stretch(0, 0, Tree[0].width, 0, Tree[0].height); /*check the width and height of the window splits for(int i = 0; i < sz; i++) cout << Tree[i].label << ' ' << Tree[i].width << ' ' << Tree[i].height << endl; */ cout << n++ << endl; for(int i = 0; i <= Tree[0].height; i++) puts(window[i]); } return 0; }
gougou第一部分结束(打算每部分4题,也不知道自己能坚持几个部分……)。