2014icpc北京个人题解

Hdu 5112-5122

 

A 签到c语言级题目 排序求最大差 唯一wa点求绝对值 样例里还体现了...现场赛应该10-15分钟内ac的题目

#include<cstdio>

#include<algorithm>

using namespace std;

struct q { int t, x; }a[10005];

int cmp(q x, q y)

{

    return x.t < y.t;

}

int main()

{

    int i, n, ca = 1;

    double ans;

    scanf("%*d");

    while (~scanf("%d", &n))

    {

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

            scanf("%d%d", &a[i].t, &a[i].x);

        sort(a, a + n, cmp), ans = 0;

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

            ans = max(ans, abs(1.*(a[i].x - a[i - 1].x) / (a[i].t - a[i - 1].t)));

        printf("Case #%d: %.2lf\n",ca++, ans);

    }

}

 

K没有算法的简单题

每个元素只能往右移动 最少移动次数就是每次找最大的 如果它不在最右就移动到最右

O(n)模拟一下就好

现场应该二三十分钟做出这题吧

#include<cstdio>

int a[1000005], i, n, ca = 1, ans, z;

int main()

{

    scanf("%*d");

    while (~scanf("%d", &n))

    {

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

            scanf("%d", a + i);

        ans = 0, z = n;

        for (i = n; i > 0; i--)

            if (a[z] != i)

                ans++;

            else

                while (a[z] >= i)

                    z--;

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

    }

}

 

H 终于出现了算法题目..

给出包含n个数的集合 求集合的子集的异或和大于m的个数 n<=40

子集是2^n个 我一直往np问题优化想去了...

简单的做法是模板背包问题 把+换成^就好 但注意+是单调的^后值可能大可能小 不能只用1个1维数组 滚动数组或者40*1e5的数组都可以 复杂度 40*1e5

题解的意思是高斯消元或trie树优化np问题 都有点难..

现场30分钟能做出就不错了...

#include<cstdio>

#include<cstring>

int a[44], dp[44][(1 << 20) + 5], i, j, n, m, ca = 1;

long long ans;

int main()

{

    scanf("%*d");

    while (~scanf("%d%d", &n, &m))

    {

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

            scanf("%d", a + i);

        memset(dp, 0, sizeof(dp)), dp[0][0] = 1;

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

        {

            for (j = (1 << 20) - 1; j >= 0; j--)

                dp[i][j] = dp[i - 1][j];

            for (j = (1 << 20) - 1; j >= 0; j--)

                dp[i][j^a[i]] += dp[i - 1][j];

        }

        for (i = m, ans = 0; i < (1 << 20); i++)

            ans += dp[n][i];

        printf("Case #%d: %lld\n", ca++, ans);

    }

}

 

I 计算几何 求2圆环面积交

容斥定理+圆面积交模板很快能过 自己推模板有点麻烦..

S = A大B大 - A大B小 - A小B大 + A小B小

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <cmath>

using namespace std;

const double eps = 1e-8;

const double PI = acos(-1.0);

struct Point

{

    double x, y;

    Point() {}

    Point(double _x, double _y)

    {

        x = _x; y = _y;

    }

    Point operator -(const Point &b) const

    {

        return Point(x - b.x, y - b.y);

    }

    double operator ^ (const Point &b) const

    {

        return x*b.y - y*b.x;

    }

    double operator * (const Point &b) const

    {

        return x*b.x + y*b.y;

    }

    void transXY(double B)

    {

        double tx = x, ty = y;

        x = tx* cos(B) - ty*sin(B);

        y = tx* sin(B) + ty*cos(B);

    }

};

double dist(Point a, Point b)

{

    return sqrt((a - b)*(a - b));

}

double Ac(Point c1, double r1, Point c2, double r2)

{

    double d = dist(c1, c2);

    if (r1 + r2 < d + eps) return 0;

    if (d < fabs(r1 - r2) + eps)

    {

        double r = min(r1, r2);

        return PI*r*r;

    }

    double x = (d*d + r1*r1 - r2*r2) / (2 * d), t1 = acos(x / r1), t2 = acos((d - x) / r2);

    return r1*r1*t1 + r2*r2*t2 - d*r1*sin(t1);

}

int main()

{

    int ca = 1;

    Point c1, c2;

    double ans, r, R, x1, y1, x2, y2;

    scanf("%*d");

    while (~scanf("%lf%lf%lf%lf%lf%lf", &r, &R, &x1, &y1, &x2, &y2))

    {

        c1.x = x1, c1.y = y1, c2.x = x2, c2.y = y2;

        ans = Ac(c1, R, c2, R) - Ac(c1, R, c2, r) - Ac(c1, r, c2, R) + Ac(c1, r, c2, r);

        printf("Case #%d: %.6lf\n", ca++, ans);

    }

}

 

到这就是做得快有可能得铜牌了..大概是模板充足简单题写的快就有牌

 

D 区间动态规划

题意很有趣

显然第一排的值一定会计算 所以不用存 记录他们的和就好 主要是第二排的操作

然而答案就是一个等式

dp[i][j]=min(dp[i][k-1]+dp[k+1][j]+a[i-1]+a[j+1]) 其中i<k<j

dp[i][j]表示消灭[i,j]的所有敌人且不消灭任何其他敌人所需的代价 k表示区间留下的最后一个敌人 显然攻击它的代价由区间以左和右的敌人贡献 枚举所有区间和k 复杂度O(n^3)

感觉dp有点递归的意思 找到一个微小的正确的关系 然后一个一个推到全局就一定是正确的 不用太在意中间的过程 电脑会自动完成的..

所以把那个式子翻译成c语言 记忆化搜索一下 果然就过了..

#include<cstdio>

#include<cstring>

#include<algorithm>

using namespace std;

int a[222], n, m, ca = 1, ans, dp[222][222];

int dfs(int i, int j)

{

    if (dp[i][j] != -1) return dp[i][j];

    if (i > j) return 0;

    int mi = 1 << 28;

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

        mi = min(mi, dfs(i, k - 1) + dfs(k + 1, j) + a[i - 1] + a[j + 1]);

    dp[i][j] = mi;

    return mi;

}

int main()

{

    scanf("%*d");

    while (~scanf("%d", &n))

    {

        memset(dp, -1, sizeof(dp)), memset(a, 0, sizeof(a)), ans = 0;

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

            scanf("%d", &m), ans += m;

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

            scanf("%d", a + i);

        printf("Case #%d: %d\n", ca++, ans + dfs(1, n));

    }

}

这题就是稳铜牌水平 写得快还有银牌 但这样的题不一能很快想到等式关系..

 

B 很有趣的智力题

给出n*m个格子和n*m个颜色(可能相同) 求是否有相邻颜色不同的铺满格子的方法 有的话输出任意一种 n,m<=5

范围很小直接dfs是可以的 但银牌题显然不会这么轻易..开始写了很zz的代码 改来改去加了剪枝也蜜汁没过

核心是 颜色最多的如果大于剩余格子的一半 则不可能铺满

重写一遍加上这一句剪枝就能过

#include<cstring>

#include<cstdio>

int a[8][8], s[29], n, m, k, f;

void dfs(int x, int y)

{

    if (x == n + 1) f = 1;

    if (f) return;

    int xx, yy, te = n*m - (x - 1)*m - y + 1;

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

        if (s[i] > (te + 1) / 2) return;

    yy = y + 1, xx = x;

    if (yy > m) yy = 1, xx++;

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

        if (s[i] > 0 && a[x - 1][y] != i&&a[x][y - 1] != i)

        {

            s[i]--, a[x][y] = i;

            dfs(xx, yy);

            if (f) break;

            s[i]++, a[x][y] = 0;

        }

}

int main()

{

    int ca = 1, i, j;

    scanf("%*d");

    while (~scanf("%d%d%d", &n, &m, &k))

    {

        memset(a, 0, sizeof(a)), memset(s, 0, sizeof(s));

        for (i = 1; i <= k; i++)

            scanf("%d", s + i);

        f = 0, dfs(1, 1);

        printf("Case #%d:\n%s\n", ca++, f ? "YES" : "NO");

        if (f)

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

            {

                printf("%d", a[i][1]);

                for (j = 2; j <= m; j++)

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

                puts("");

            }

    }

}

另一种神奇的方法是根据那句话构造 只要一开始最多的颜色不大于(n*m+1)/2 就一定存在方案 但直接O(n*m)构造有点麻烦 特判情况太多..(或者没想到合适的构造方法)

 

做出上面所有就是稳银牌 多做任意一道其他题就稳金牌

 

F 概率

给n个灯m个开关 每个开关控制一系列灯 每个开关有0.5的概率开 x为最后亮灯的个数 求E(x^3)*2^m%(1e9+7)

数学+dp+状态压缩 算法倒没什么新奇的..

首先2^m是计算式子的分母 正好抵消掉 只考虑E(x^3)

考虑x是xi的和(i=1..n 0表示灭1表示亮)

x^3=(xi的和)* (xi的和)*(xi的和)=xi*xj*xk的和 1<=i,j,k<=n

所以只用要能求出xijk都为1时的E(xi*xj*xk) 然后三重循环ijk求和就好

能想到这就没什么问题了 状态压缩3个灯的状态和每个开关对灯的操作 和H一模一样的背包 依然是亦或(亦或正好和开灯关灯的模拟步骤完全一样)

总复杂度O(n^3*m) 前面有背包空间的常数2^3 再乘以循环里的常数4(memset和memcpy) 算下来正好是2亿 3秒时限刚好

#include<cstdio>

#include<cstring>

#define mm 1000000007

int n, m, a[55][55];

long long dp[55][9], ans;

int main()

{

    int i, j, k, ii, jj, te, x, ca = 1;

    scanf("%*d");

    while (~scanf("%d%d", &n, &m))

    {

        memset(a, 0, sizeof(a)), memset(dp, 0, sizeof(dp)), ans = 0;

        for (i = 1; i <= m; i++)

        {

            scanf("%d", &k);

            while (k--&&scanf("%d", &x))

                a[i][x] = 1;

        }

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

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

                for (k = 1; k <= n; k++)

                {

                    memset(dp, 0, sizeof(dp));

                    dp[0][0] = 1;

                    for (ii = 1; ii <= m; ii++)

                    {

                        te = a[ii][i] * 4 + a[ii][j] * 2 + a[ii][k];

                        memcpy(dp[ii], dp[ii - 1], sizeof(dp[ii]));

                        for (jj = 0; jj < 8; jj++)

                            dp[ii][jj^te] = (dp[ii][jj^te] + dp[ii - 1][jj]) % mm;

                    }

                    ans = (ans + dp[m][7]) % mm;

                }

        printf("Case #%d: %lld\n", ca++, ans);

    }

}

虽然是金牌难度的题但算法只涉及背包dp 数学分析那块有点难..

 

其他的题就难了..看文字题解也一时半会写不出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值