2012 East Central Regional Contest 解题报告

昨晚各种莫名其妙卡题。

不过细看这套题还挺简单的。全是各种暴力。

除了最后一道题计算几何看起来很麻烦的样子,其他题都是很好写的吧。

A. Babs' Box Boutique

题目大意是给出不超过10个的长方体,然后求怎样堆叠使得放的长方体最多。 堆叠的要求是长方体一个一个的往上放,要求接触的面,上面的面长和宽不能比下面的面大

那么每个长方体有三个面,我们就3^n枚举每个长方体使用的哪一面放的。

然后按照这些面的宽进行排序,做一下LIS即可

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <string>

#include <map>

#include <set>

#include <vector>

#include <cmath>

#include <algorithm>

#define eps 1e-8

#define INF 10000005

using namespace std;

struct Rec

{

    int a[4];

}p[12];

pair<int, int> s[12], t[12];

int n, ans;

int dp[12];

int getans()

{

    for(int i = 0; i < n; i++) t[i] = s[i];

    sort(t, t + n);

    int tmp = 0;

    for(int i = 0; i < n; i++)

    {

        dp[i] = 1;

        for(int j = 0; j < i; j++)

            if(t[i].second >= t[j].second)

                dp[i] = max(dp[i], dp[j] + 1);

        tmp = max(tmp, dp[i]);

    }

    return tmp;

}

void dfs(int deep)

{

    if(deep == n)

    {

        int tmp = getans();

        ans = max(tmp, ans);

        return;

    }

    s[deep] = make_pair(p[deep].a[0], p[deep].a[1]);

    dfs(deep + 1);

    s[deep] = make_pair(p[deep].a[1], p[deep].a[2]);

    dfs(deep + 1);

    s[deep] = make_pair(p[deep].a[0], p[deep].a[2]);

    dfs(deep + 1);

}

int main()

{

    int cas = 0;

    while(scanf("%d", &n) != EOF && n)

    {

        for(int i = 0; i < n; i++)

        {

            for(int j = 0; j < 3; j++)

                scanf("%d", &p[i].a[j]);

            sort(p[i].a, p[i].a + 3);

        }

        ans = 0;

        dfs(0);

        printf("Case %d: %d\n", ++cas, ans);

    }

    return 0;

}


 

B. Flash Mob

这题的大意就是二维平面有若干个点。

现在要让他们都移动到某个点上(曼哈顿距离)

问怎样花费总和最小


这题很眼熟。

多校貌似做过吧。

X,Y分开统计。

X就枚举点中所有的x坐标。 然后写一下总和的式子就发现,可以用部分和做到O(1)时间求出总花费

同理Y

 

#include <iostream>

#include <cstdio>

#include <algorithm>

#define MAXN 1111

using namespace std;

typedef long long LL;

LL x[MAXN], y[MAXN];

LL sumx[MAXN], sumy[MAXN];

int n, cas;

int main()

{

    while(scanf("%d", &n) != EOF && n)

    {

        for(int i = 1; i <= n; i++) scanf("%lld%lld", &x[i], &y[i]);

        sort(x + 1, x + n + 1); sort(y + 1, y + n + 1);

        sumx[0] = sumy[0] = 0;

        for(int i = 1; i <= n; i++) sumx[i] = sumx[i - 1] + x[i], sumy[i] = sumy[i - 1] + y[i];

        LL ansx = -1, ansy = -1, resx = 1000000009LL, resy = 1000000009LL;

        for(int i = 1; i <= n; i++)

        {

            LL tmp = x[i] * i - sumx[i] + sumx[n] - sumx[i] - (n - i) * x[i];

            if(resx > tmp) ansx = x[i], resx = tmp;

            tmp = y[i] * i - sumy[i] + sumy[n] - sumy[i] - (n - i) * y[i];

            if(resy > tmp) ansy = y[i], resy = tmp;

        }

        printf("Case %d: (%lld,%lld) %lld\n", ++cas, ansx, ansy, resx + resy);

    }

    return 0;

}


 

C. Hexagon Perplexagon

题意的话

就是给出7个6边形中每个边对应的值,然后每个6边形给的值是按照顺时针方向给出的。 然后这个六边形是可以旋转的。

现在要用这7个6边形,构成图中所示的图形,要求每个六边形与其他六边形邻接边上的数要相同。每个六边形自身可以旋转然后放进去。

那么我刚开始的思路是7!处理出所有的情况,然后判断可行性。结果TLE了。

后来发现case有1W多个。

那么就改成了DFS。

按照图中所示的顺序进行DFS。 然后根据中心的那个六边形,调整新六边形,旋转到跟中间那个邻接的边上数一样,之后就判断跟其他邻接的六边形是不是有冲突。

这样就能剪枝掉很多了

 

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cstring>

#define MAXN 1111

using namespace std;

int a[8][8], b[8], adj[8][8];

int has, used[8];

int off[] = {0, 3, 4, 5, 0, 1, 2};

int cp[] = {2, 5, 3, 0, 4, 1, 5, 2, 0, 3, 1, 4};

void dfs(int deep)

{

    if(has) return;

    if(deep == 7)

    {

        has = 1;

        for(int i = 0; i < 7; i++)

            printf(" %d", b[i]);

        return;

    }

    for(int i = 0; i < 7; i++)

        if(!used[i])

        {

            used[i] = 1;

            b[deep] = i;

            int now;

            if(deep == 0) now = 1;

            else now = adj[0][deep - 1];

            int pos = 0;

            for(int j = 0; j < 6; j++)

                if(a[i][j] == now) pos = j;

            for(int j = 0; j < 6; j++)

                adj[deep][(j + 6 - pos + off[deep]) % 6] = a[i][j];

            int nt = deep - 1;

            bool flag = true;

            if(deep > 1) flag &= (adj[deep][cp[nt * 2 - 1]] == adj[nt][cp[nt * 2 - 2]]);

            if(deep == 6) flag &= (adj[6][1] == adj[1][4]);

            if(flag)

                dfs(deep + 1);

            used[i] = 0;

        }

}

int main()

{

    int T, cas = 0;

    scanf("%d", &T);

    while(T--)

    {

        for(int i = 0; i < 7; i++)

            for(int j = 0; j < 6; j++)

                scanf("%d", &a[i][j]);

        has = 0;

        memset(used, 0, sizeof(used));

        printf("Case %d:", ++cas);

        dfs(0);

        if(!has) printf(" No solution");

        printf("\n");

    }

    return 0;

}


 

D.I've Got Your Back(gammon)

这题应该算是签到题目了。 

题意就是用15,拆分到6个位置上。

有两种询问,一个是给出某种拆分,问这个拆分在所有拆分中排第几。

另一个就是输出某个拆分。

我就直接DFS除了所有可能的拆分,然后map映射了

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <string>

#include <algorithm>

#include <map>

using namespace std;

char op[5];

int a[7];

int cnt, x;

map<string, int>mp;

struct P{

    int a[6];

}ans[22222];

bool cmp(P x, P y)

{

    for(int i = 0; i < 6; i++)

        if(x.a[i] < y.a[i]) return true;

        else if(x.a[i] > y.a[i]) return false;

}

void dfs(int sum, int pos)

{

    if(pos == 0)

    {

        a[pos] = sum;

        for(int i = 0; i < 6; i++)

            ans[cnt].a[i] = a[i];

        cnt++;



        return;

    }

    for(int i = sum; i >= 0; i--)

    {

        a[pos] = i;

        dfs(sum - i, pos - 1);

    }

}

int main()

{

    dfs(15, 5);

    sort(ans, ans + cnt, cmp);

    for(int i = 0; i < cnt; i++)

    {

        string tmp = "";

        for(int j = 0; j < 6; j++)

        {

            tmp = tmp + (char)(ans[i].a[j] + '0');

            tmp += ' ';

        }

        mp[tmp] = i;

    }

    int cas = 0;

    while(scanf("%s", op) != EOF)

    {

        if(op[0] == 'e') break;

        printf("Case %d: ", ++cas);

        if(op[0] == 'm')

        {

            string tmp = "";

            for(int i = 0; i < 6; i++) scanf("%d", &a[i]);

            for(int i = 0; i < 6; i++)

            {

                tmp = tmp + (char)(a[i] + '0');

                tmp += ' ';

            }

            printf("%d\n", mp[tmp]);

        }

        else

        {

            scanf("%d", &x);

            for(int i = 0; i < 5; i++) printf("%d ", ans[x].a[i]);

            printf("%d\n", ans[x].a[5]);

        }

    }

    return 0;

}


E. Parencedence!

 

题意就是给出一个运算序列。

进行一个游戏,有两个玩家。

对于每一步,玩家可以在任意某个运算符和其旁边的两个数加上括号,将其算出来。使得运算序列变成新的运算序列。

然后玩家一的目的是让这个序列最终的值变的越大越好

玩家二正好相反。

最后输出二者各自先手所能达到的最优值,并输出对应第一步他们干了什么


思路的话。可以发现运算符只有9个。

那么就可以进行DFS了

选取序列中某个运算符以及其旁边的两个数进行计算,生成新的序列。直至获得最终结果

当然,因为有两个玩家,所以要有所区分。

当玩家一下手的时候,就是取最大值。

当玩家二下手的时候,就是取最小值。

 

#include <iostream>

#include <algorithm>

#include <cstring>

#include <cstdio>

#define MAXN 55

#define INF 1000000007

using namespace std;

int rval[11], n;

char ss[12][5];

int rop[11];

typedef pair<int, int> P;

int getval(int x, int y, char c)

{

    switch(c)

    {

        case '+' : return x + y;

        case '*' : return x * y;

        case '-' : return x - y;

    }

}

P get(int val[], int op[], int deep, int flag)

{

    P ans;

    if(deep == 0)

    {

        ans.first = val[0];

        ans.second = -1;

        return ans;

    }



    if(flag) ans.first = -INF;

    else ans.first = INF;



    for(int i = 0; i < deep; i++)

    {



        int x = getval(val[i], val[i + 1], ss[op[i]][0]);

        int cnt = 0;

        int tmpop[11], tmpval[11];

        for(int j = 0; j <= deep; j++)

        {

            if(j == i) continue;

            if(j == i + 1) tmpval[cnt++] = x;

            else tmpval[cnt++] = val[j];

        }



        cnt = 0;

        for(int j = 0; j < deep; j++)

            if(j == i) continue;

            else tmpop[cnt++] = op[j];

        P tmp = get(tmpval, tmpop, deep - 1, !flag);

        if(flag)

        {

            if(tmp.first > ans.first)

            {

                ans.first = tmp.first;

                ans.second = i;

            }

        }

        else

        {

            if(tmp.first < ans.first)

            {

                ans.first = tmp.first;

                ans.second = i;

            }

        }

    }

    return ans;

}



int main()

{

    int T, cas = 0;

    scanf("%d", &T);

    while(T--)

    {

        scanf("%d", &n);

        for(int i = 0; i < n; i++) scanf("%d%s", &rval[i], ss[i]);

        for(int i = 0; i < n; i++) rop[i] = i;

        scanf("%d", &rval[n]);

        P ansx = get(rval, rop, n, 1);

        P ansy = get(rval, rop, n, 0);

        printf("Case %d:\n", ++cas);

        printf("Player 1 (%d%s%d) leads to %d\n", rval[ansx.second], ss[ansx.second], rval[ansx.second + 1], ansx.first);

        printf("Player 2 (%d%s%d) leads to %d\n", rval[ansy.second], ss[ansy.second], rval[ansy.second + 1], ansy.first);

        if(ansx.first > -ansy.first) printf("Player 1 wins\n");

        else if(ansx.first < -ansy.first) printf("Player 2 wins\n");

        else printf("Tie\n");

    }

    return 0;

}


F. Road Series

 

题目的大意就是

给出n个信息。

这些信息有字母标点,数字构成。

然后我们可以从这些信息中找出数字,每个串中找到的数字会被总的记录下来,不会被遗忘。首先的要求是这个数是该信息串的一个子串。

然后还有一个要求就是

定义x为最后完成的数。

意思就是如果0到x-1的数字都被记录下来了, 那么x就是最后完成的数。

然后x是随时都有可能改变的,因为我们随时都会记录新的数字。 如果导致某个数y < x且 0~y-1的数字都被记录了,那么x就要更新为y。

定义w为一个窗口值

意思就是你能找到的数字必须在x + w范围内。

 刚开始的时候x是0

然后注意题目是一个串一个串给出来的。

对于某个串,是这样一个过程:先把x + w范围内的数都找到了,然后记下来,如果已经记录的数使得x发生了变化,也就是增大了。那么我们可以记录的数字的范围又变大了。我们就可能在这个串中找到新的数字,然后记录下来。也就是x在一个串中可能会被多次改变。

题目中要求的第二个值,就是我们记录的数字中最大那个。 


那么本题的思路是这样的:

首先我们要观察x在一个串中到底能最多涨多少。

因为如果我们按题意那样,等着x发生变化了再去找一遍数字。复杂度就有点略高了。我们可以想象。如果w ==1时,这将会对一个串进行很多次的扫描。

所以必须先将可能会被记录的数字处理出来。这就需要知道x在一个串中到底对多涨多少。

题目中说每个串长度不超1000

那么我们理想化一下。

一个串中包含了1~1000的所有数字。

最优长度是多少呢?

1~1000内有900个三位数。

如果说每一位数字往后数两位都能构成一个唯一的三位数的话。

这就需要900位了。

更何况其中平均每100个数中有接近20个带有0数字的三位数。

这就使得1000长度的串是无论如何也无法包含1~1000的数字的。

就更别说更大的区间长度为1000的数了


所以呢。x是无论如何也涨不到1000以上的。

我们每次就把一个串中,不超过x 1000大小的数先预处理出来,如果这个数是num,我们可以将num - x标记出来。

然后就可以观察x到底能涨到多少了。x涨完之后,再将x + w以内的数也是以num-x的形式记录下来。 因为下个串也有可能用到以前串记录的数。


 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <string>

#include <map>

#include <set>

#include <vector>

#include <cmath>

#include <algorithm>

#define eps 1e-8

#define INF 10000005

using namespace std;

int n, w;

char s[1111];

int vis[1111];

int now;

void gao(string str)

{

    int len = str.size();

    for(int i = 0; i < len; i++)

    {

        int sum = 0;

        for(int j = 0; j < 6; j++)

            if(i + j < len)

            {

                sum = sum * 10 + (str[i + j] - '0');

                if(sum > now && sum <= now + 1000)

                    vis[sum - now] = 1;

            }

    }

}

void split()

{

    int len = strlen(s);

    int flag= 0;

    string tmp = "";

    for(int i = 0; i < len; i++)

    {

        if(s[i] >= '0' && s[i] <= '9')

        {

            tmp = tmp + s[i];

            flag = 1;

        }

        else if(flag)

        {

            gao(tmp);

            tmp = "";

            flag = 0;

        }

    }

    if(flag) gao(tmp);

}

int main()

{

    int T;

    scanf("%d", &T);

    int cas = 0;

    while(T--)

    {

        scanf("%d%d", &n, &w);

        getchar();

        memset(vis, 0, sizeof(vis));

        now = 0;

        int ans = 0;

        while(n--)

        {

            gets(s);

            split();

            int tnow = now;

            for(int i = 1; i <= 1000; i++)

                if(vis[i]) tnow++;

                else break;

            for(int i = 1; i <= 1000; i++)

            {

                int pos = i + tnow - now;

                if(pos <= tnow + w - now) vis[i] = vis[pos];

                else vis[i] = 0;

            }

            now = tnow;

        }

        ans = now;

        for(int i = 1; i <= w; i++)

            if(vis[i]) ans = now + i;

        printf("Case %d: %d %d\n", ++cas, now, ans);

    }

    return 0;

}


 

G.Show Me the Money

这题题意就是给出了若干货币的汇率

然后我们需要若干个某种货币,

但是手头上只有别的货币,问怎样可以用别的货币换得的这种货币最接近我们要的数量,并且不能比我们需要的数量少。

注意用别的货币时数量不能大于10W,且是整数。

这题的坑爹地方就是说。

题目中说任意一对货币汇率只给出一次,也就是没有重边。

并且不会出现钱越换越多的情况。

但是实际上在求任意两种货币汇率的时候

我用的是floyd。 没有加min就WA了很多次。加了min就过了。很是奇怪

这应该跟题目要求的答案没有关系。

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <string>

#include <map>

#include <cmath>

#define eps 1e-8

#define INF 10000005

using namespace std;

char sa[22], sb[22];

int a, b, n;

string bi[22];

double g[22][22];

map<string, int>mp;

double num;

char name[22];

int cnt ;

void floyd()

{

    for(int k = 1; k <= cnt; k++)

    for(int i = 1; i <= cnt; i++)

        for(int j = 1; j <= cnt; j++)

            if(g[i][k] != INF && g[k][j] != INF)

                g[i][j] = min(g[i][j], g[i][k] * g[k][j]);



}

int main()

{

    int cas = 0;

    while(scanf("%d", &n) != EOF && n)

    {

        mp.clear();

        cnt = 0;

        for(int i = 1; i <= 20; i++)

            for(int j = 1; j <= 20; j++)

                g[i][j] = INF;

        for(int i = 1; i <= 20; i++)

            g[i][i] = 1;

        for(int i = 0; i < n; i++)

        {

            scanf("%d%s%*s%d%s", &a, sa, &b, sb);

            if(mp[sa] == 0) mp[sa] = ++cnt;

            if(mp[sb] == 0) mp[sb] = ++cnt;

            int ia = mp[sa], ib = mp[sb];

            bi[ia] = sa;

            bi[ib] = sb;

            g[ia][ib] = (double)b / (double)a;

            g[ib][ia] = (double)a / (double)b;

        }

        scanf("%lf%s", &num, name);

        int id = mp[name];

        floyd();

        double mi = INF;

        int ans;

        int res;

        for(int i = 1; i <= cnt; i++)

            if(id != i && g[id][i] != -1)

            {

                int x = (int)ceil(num * g[id][i] - eps);

                if(x > 100000) continue;

                double y = (double)x * g[i][id];

                if(mi > y)

                {

                    mi = y;

                    ans = i;

                    res = x;

                }

            }

        printf("Case %d: ", ++cas);

        printf("%d %s\n", res, bi[ans].c_str());

    }

    return 0;

}


 

H.Sofa, So Good

这题目我想了一个结论,竟然就过了

题意是。

有若干个工人。

有若干个工件。

每个工件的工作分为两阶段,一阶段完了二阶段才能开始。

然后题目给出了每个工人对每个工件的一二阶段花费的时间。

注意每个工人一二阶段面对的工件可能会不一样。。

然后问最后消耗的总时间最少情况下,每个工人的工作分配是什么。

我的想法是:

对于每个工件的阶段一。我们先求一个阶段一花费总时间最少的安排。用KM求一下

然后根据阶段一的安排。再对二阶段进行建图。

这样就过了,但是不知道为什么,也不会证明。

 

#include <iostream>

#include <algorithm>

#include <cstring>

#include <string>

#include <cstdio>

#include <cmath>

#include <queue>

#include <map>

#include <set>

#define eps 1e-5

#define MAXN 55

#define MAXM 55

#define INF 100000007

using namespace std;

int n, m, ny, nx;

int w[MAXN][MAXM], tw[MAXN][MAXM];

int lx[MAXN], ly[MAXM];

int linky[MAXM];

int visx[MAXN], visy[MAXM];

int slack[MAXM];

int bx[55][55], ax[55][55];

int ans[55][3];

bool find(int x)

{

    visx[x] = 1;

    for(int y = 1; y <= ny; y++)

    {

        if(visy[y]) continue;

        int t = lx[x] + ly[y] - w[x][y];

        if(t == 0)

        {

            visy[y] = 1;

            if(linky[y] == -1 || find(linky[y]))

            {

                linky[y] = x;

                return true;

            }

        }

         else if(slack[y] > t) slack[y] = t;

    }

    return false;

}

int KM()

{

    memset(linky, -1, sizeof(linky));

    for(int i = 1; i <= nx; i++) lx[i] = -INF;

    memset(ly, 0, sizeof(ly));

    for(int i = 1; i <= nx; i++)

        for(int j = 1; j <= ny; j++)

            if(w[i][j] > lx[i]) lx[i] = w[i][j];

    for(int x = 1; x <= nx; x++)

    {

        for(int i = 1; i <= ny; i++) slack[i] = INF;

        while(true)

        {

            memset(visx, 0, sizeof(visx));

            memset(visy, 0, sizeof(visy));

            if(find(x)) break;

            int d = INF;

            for(int i = 1; i <= ny; i++)

                if(!visy[i]) d = min(d, slack[i]);

            if(d == INF) return -1;

            for(int i = 1; i <= nx; i++)

                if(visx[i]) lx[i] -=d;

            for(int i = 1; i <= ny; i++)

                if(visy[i]) ly[i] += d;

                    else slack[i] -= d;

        }

    }

    int tp = 0, cnt = 0;

    for(int i = 1; i <= ny; i++)

        if(linky[i] != -1 && w[linky[i]][i] != -INF)

        {

            tp += w[linky[i]][i];

            cnt++;

        }

    if(cnt != nx) return -1;

    return -tp;

}

int main()

{

    int cas = 0;

    while(scanf("%d", &n) != EOF && n)

    {

        nx = ny = n;

        for(int i = 1; i <= n; i++)

            for(int j = 1; j <= n; j++)

            {

                scanf("%d", &ax[i][j]);

                w[i][j] = -ax[i][j];

            }

        for(int i = 1; i <= n; i++)

            for(int j = 1; j <= n; j++)

                scanf("%d", &bx[i][j]);

        int res = KM();

        int val[55];

        for(int i = 1; i <= n; i++)

            val[linky[i]] = -w[linky[i]][i], ans[linky[i]][0] = i;

        for(int i = 1; i <= n; i++)

            for(int j = 1; j <= n; j++)

                tw[i][j] = w[i][j];

        for(int i = 1; i <= n; i++)

            for(int j = 1; j <= n; j++)

            {

                int tmp = max(-tw[linky[j]][j], val[i]);

                w[i][j] = -(tmp + bx[i][j]);

            }

        res = KM();

        for(int i = 1; i <= n; i++)

            ans[linky[i]][1] = i, ans[linky[i]][2] = -w[linky[i]][i];

        int sum = 0;

        for(int i = 1; i <= n; i++)

        {

            int t0 = ans[i][0];

            int t1 = ans[i][1];

            sum += ans[i][2] - (ax[i][t0] + bx[i][t1]);

        }

        printf("Case %d:\n", ++cas);

        for(int i = 1; i <= n; i++)

        {

            printf("Worker %d: %d %d %d\n", i, ans[i][0], ans[i][1], ans[i][2]);

        }

        printf("Total idle time: %d\n", sum);

    }

    return 0;

}


 

I.Town Square

大意就是给出四个点。

然后求一个正方形,使得四个点分别在四个边上。

问边长最小是多少。

标程260行。 还有人400行过的。

计算几何神题啊。 目测不会。

 

你可能感兴趣的:(test)