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题,也不知道自己能坚持几个部分……)。

posted @ 2013-05-17 14:26  zxfx100  阅读(174)  评论(0编辑  收藏  举报