Benelux Algorithm Programming Contest 2014 Final

Benelux Algorithm Programming Contest 2014 Final

G - Growling Gears

4 a c b 2 4 a 最大的就可以

B - Button Bashing

这题当时想多了,第一反应是一个背包,如果这题没有上下界的限制的话,用背包其实是可以的,可以把按键增加的时间从大到小排序,让加时的先做,减时的后做背包,这样都不用把容量加上3600了。

但其实这题用spfa做就可以,因为n是在太小了。。。

#include <bits/stdc++.h>

using namespace std;
const int MAXN  = 20;
int v[MAXN];
int n,t;
int vis[4000];
bool inque[4000];

void spfa()
{
    memset(vis,0x3f,sizeof vis);
    queue<int>que;
    vis[0] = 0;
    que.push(0);
    while (!que.empty())
    {
        int u = que.front();que.pop();
        inque[u] = false;
        for (int i=1;i<=n;i++)
        {
            int to = u + v[i];
            if (to > 3600) to = 3600;
            else if (to < 0) to = 0;
            if (vis[to] > vis[u] + 1)
            {
                vis[to] = vis[u] +1 ;
                if (!inque[to])
                {
                    que.push(to);
                    inque[to] = true;
                }
            }
        }
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&t);
        for (int i=1;i<=n;i++) scanf("%d",&v[i]);
        spfa();
        for (int i=t;i<=3600;i++)
        {
            if (vis[i] != 0x3f3f3f3f)
            {
                printf("%d %d\n",vis[i],i - t);
                break;
            }
        }
    }
    return 0;
}

J - Jury Jeopardy

用一个大点的地图,把所有走过的路都用.标出来,其他地方都用#表示,找出一个上右下侧都是#的矩形,并且包围那个路径就可以了。

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 210;
char mp[400][400];
string s;

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        cin >> s;
        memset(mp,0,sizeof mp);
        int x = 200,y = 1,face = 0;
        mp[x][y] = '.';
        for (int i=0;i<s.length();i++)
        {
            if (s[i] == 'B')face = (face + 2)%4;
            if (s[i] == 'R') face = (face+1)%4;
            if (s[i] == 'L') face = ((face-1)%4+4)%4;
            if (face == 0) y++;
            if (face == 1) x++;
            if (face == 2) y--;
            if (face == 3) x--;
            mp[x][y] = '.';
        }
        int stx = 0,edx = 0,sty = 0,edy = 0;
        for (int i=1;i<=400 && !stx;i++)
        {
            for (int j=1;j<=400;j++)
                if (mp[i][j] =='.')
            {
                stx = i-1;
                break;
            }
        }
        for (int i=stx+1;i<=400;i++)
        {
            bool flag = false;
            for (int j=1;j<=400;j++)
                if (mp[i][j] == '.') flag = true;
            if (flag == false)
            {
                edx = i;
                break;
            }
        }
        for (int i=1;i<=400;i++)
        {
            bool flag = false;
            for (int j=1;j<=400;j++)
                if (mp[j][i] == '.') flag = true;
            if (!flag)
            {
                edy = i;
                break;
            }
        }
        printf("%d %d\n",edx-stx+1,edy);
        for (int i=stx;i<=edx;i++)
        {
            for (int j=1;j<=edy;j++)
            {
                if (mp[i][j] == 0) mp[i][j] = '#';
                printf("%c",mp[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

I - Interesting Integers

一个数字由a,b为初始的fibonacci数列组成

列一下算式就会发现,这个数字是由a*x + b*y组成,并且x,y是两个相邻fibonacci数,由于最大就1e9,所以枚举一下也很快,可以从y较大开始枚举,这样b的范围就会变小。

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 101000;
const int INF = 0x3f3f3f3f;
int fib[MAXN];

int main()
{
    fib[1] = 1;
    fib[2] = 1;
    for (int i=3;i<=44;i++) fib[i] = fib[i-2] + fib[i-1];
    int t,n;
    cin >> t;
    while (t--)
    {
        cin >> n;
        int gcd,x,y;
        int ansx = INF,ansy = INF;
        for (int i=44;i>=2;i--)
        {
            if (fib[i] > n) continue;
            long long a = fib[i-1],b = fib[i];
            for (int j=1;j<=ansy;j++)
            {
                if ( b * j >= n) break;
                if ( (n - b*j) % a == 0)
                {
                    int tmpx = (n - b*j) / a;
                    if (tmpx > j) continue;
                    if (j < ansy)
                    {
                        ansy = j;
                        ansx = tmpx;
                    }else if ( j == ansy && tmpx < ansx)
                    {
                        ansx = tmpx;
                        ansy = j;
                    }
                }
            }
        }
        printf("%d %d\n",ansx,ansy);
    }
    return 0;
}

E - Excellent Engineers

比赛的时候想到了先把第一维排个序,然后从第一维的第一名到第n名这样考虑,所以要维护一个2维树状数组,来判断第二维第三维都比你小的有没有,但是明显空间不够。然后就卡死了。。

看题解后知道了要维护一个叫做Pareto front 的东西,如下图,我们要维护的是下面的一条,在黄色区域的代表不能选择的点,准确来说,这些点要在e之后放入图中,才不能选择,否则也是可以选的,因为如果在e之前放入,说明它们的第一维比e要小。如果是在e之前放入,这些可能被选,但是更新pareto front的时候这些点是要删掉的,也就是它们在可能在名单上,但是肯定不在pareto front里面。
Pareto front

先看x坐标,代表的是第二维的排名,y坐标代表第三维排名,可以看出,如果有人的x坐标比你小,并且y也比你小,那么你肯定就不能在pareto front了,所以这条线从左到右是一个递降的趋势。

那么维护的时候,先判断是否需要放入,如果需要放入,那么再把原来在集合中两个维度都大于它的删掉,然后再插入它,

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 100010;
struct node
{
    int a,b,c;
}p[MAXN];

bool cmp1(const node &a,const node &b)
{
    return a.a < b.a;
}

bool cmp2(const node &a,const node &b)
{
    return a.b < b.b;
}

typedef set<node,bool(*)(const node &,const node &)> myset;
typedef myset::iterator  IT;

int n;

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int ans = 0;
        myset e1(cmp1),e2(cmp2);
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
            e1.insert(p[i]);
        }
        for (IT it1 = e1.begin();it1!=e1.end();it1++)
        {
            bool putin = false;
            IT it2 = e2.lower_bound(*it1);
            if (it2 == e2.begin()) putin = true;
            else
            {
                it2--;
                if (it1->c < it2->c ) putin = true;
                it2++;
            }
            if (!putin) continue;
            IT a = it2,b = it2;
            while (b!=e2.end() && b->c > it1->c)b++;
            e2.erase(a,b);
            e2.insert(*it1);
            ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

D - Dropping Directions

这题当时是想到找环了,但是具体怎么处理没想好。

仔细一想的话会发现,这个图上的环是在太多了,感觉都可以当成,一个球体表面上切成了一个个矩形一样来看了。每条边只有两种走法,也就是顺逆。我一开始还在想会不会两个环重叠,这样只要一个指示牌就可以了,但举了举例子后发现这是不可能的,因为只要重合了,他们的走法就肯定会一样,所以不可能走出两个环来。那么只要根据他给的图,把所有路都走一遍,数一下有几个不包含g的环就可以了。

看了这题,我感觉做题目的时候还是要学会看出题的特殊性,找出图的特殊性,然后从这点下手去解决问题。

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 100100;
int n,g,t;
int dir[MAXN][4];
bool vis[MAXN][4];

int main()
{
    scanf("%d",&t);
    while (t--)
    {
        memset(vis,0,sizeof vis);
        scanf("%d%d",&n,&g);
        for (int i=1;i<=n;i++)
            for (int j=0;j<4;j++)
                scanf("%d",&dir[i][j]);
        int post = 0;
        for (int i=1;i<=n;i++)
            for (int j=0;j<4;j++)
                if (!vis[i][j])
                {
                    int curi = i,curj = j;
                    bool foundg = false;
                    while (1)
                    {
                        if (curi == g) foundg = true;
                        int nexi = dir[curi][ (curj + 2)%4 ];
                        int nexj;
                        for (int k=0;k<4;k++) if ( dir[nexi][k] == curi ) nexj = k;
                        if (vis[curi][curj] && vis[curi][ (curj + 2)%4 ]) break;
                        vis[curi][curj] = true;
                        vis[curi][ (curj + 2)%4 ] = true;
                        curi = nexi;
                        curj = nexj;
                    }
                    if (!foundg)
                        post ++;
                }
        printf("%d\n",post);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/z631681297/article/details/80998215