TopCoder SRM288 DIV1 Report

TopCoder SRM288 DIV1 Report

match date:Wednesday, February 8, 2006
report date:Friday, February 10, 2006

Preface:
    也许是第一次成绩太好了,第二次被分到了div 1 组,才发觉自己的水平真得不济。只能有时间coding两道题目,而第二题因为没把握所以没提交。第一题也没通过system test。在chanllenge阶段试了3道题目,结果无一成功,真是落泊啊!
    总体情况是第一题有阴险数据,所以成功率不高,第二题其实用简单dp就行了,第三题是难题,挑战者也偏少。rating落到了1001,下次还是div 2 啊。马上要开学,机会不多了,但我会抽空来参加的。
   
Problem 250 - FindTriangle:
    是一道几何题,推简单的公式没有花掉我多少时间,但是精度误差一直是困难所在。在比赛中用了BigDecimal,但最后还是没能通过system test。

    首先,计算三角形面积的公式有比较著名的"Heron's formula":
        A = 1/4 * Sqrt((a + b + c) * (a + b - c) * (a + c - b) * (b + c - a))
    但在这题中最好不要用,计算距离a b c需要开方,而最后还要开方一次,这样精度就不够好了。

    注意到计算Area还有一个计算公式 A=    ½ |u × v| ,u和v是三角形两条边的向量,求u和v只需要加减就可以求得。而叉乘只要根据定义通过加和乘就可以求得。只有在最后取模用到一次开方。因此,精度是可以保证的。

    u.x= p1.x-p2.x  u.y=p1.y-p2.y  u.z=p1.z-p2.z
    v.x= p2.x-p3.x  v.y=p2.y-p3.y  v.z=p2.z-p3.z
    |u × v|^2 = (u.y * v.z - u.z * v.y) ^2+ (u.x * v.z - u.z * v.x)^2 +(u.x * v.y - u.y * v.x)^2

    找到两到ACM题可以训练: PKU 2079(光会算三角形面积不够,数据量很大会超时),SHANTOU OJ 1061(二维的,简单)

Problem 450- CannonBalls:
    哎,看来是经验不足啊,最简单的DP错看成贪心,等醒过来就来不及了。(还要继续学习学习再学习)
要求数列,直接循环累加就行了。关键是如何将参数n划分最少的块。
    DP方程如下:
         f (n) = min( k=(1,n) && k is a valid divider f (n-k)+1 )        f (0)=0

然后直接取f (n) 便是结果了。

    在赛后分析中,给出了用递归的解决方法(其实满象DFS的,具体是从原初贪心的方向开始逐渐比较找出最优解,其中使用一个v变量剪支),代码上比DP难写得多。
    在学习完赛后分析后,我把我的递归程序给出来(去掉了赛后分析的程序一些饶口地方):

//    n 是 待划分数, st 当前的划分块的下表 , v是目前最优的可能解(剪支用)
public int dfs(int n, int st, int v) {
        if (n == 0)    return 0;                // 递归的终止,解集树叶子处
        if (st == 1)    return n;                // 无法继续分析,返回最糟解n
        int best = n;                        // 暂时最优解为n
        while (n < t[st] && st > 2)            // 找小于n的最大的一个划分块
            st--;
        int div = n / t[st];                    // div是最小可能划分数
        if (div >= v)                        // 若当前最小的可能划分数都大于
            return n;                        // 已最优解,那么剪掉
        for (int i = div; i >= 0; i--) {            // 从大到小的递归类似不断贪心求解
            v = Math.min(best, v);            // 与前次循环的解比较,更新最优解
            best = Math.min(best, i + dfs(n - i * t[st], st - 1, v - i));        // 递归
            if (best == 1)                   
                return 1;                    // best为1 已经是最小的解了,返回
        }
        return best;
    }



Problem 1000- CountryWar:
    到底是div 1的最后一题呀,在题目分类中写的是DP+GraphTheory。分析程序中用mask bit来处理两节点相邻的表示,用DP预先计算所有战争的胜率,最后搜索找最优解。(-_-o)
接着我对几个要点说明一下,如果觉得不够的话,可以去看看赛后分析(写得满清晰):

    搜索前的准备工作是先DP出所有战争的胜率,prob[attacker][defender][remain] 记录:

        几个变量说明 a攻击方值 b防御方值 r 剩下的值(剩余士兵数)
            win(a,b) 返回胜率(就是a2/(a2+a*b+b2))
            battle[a][b]数组 记录特定情况下a与b战斗的胜率,主要DP在这吧。
                    注意!battle值对每次求prob都在变的,所以每求一次prob都要重求一遍battle。
                        ba(i, j-1) += ba(i, j) * win (i, j)       ba(i,j)初始0 ba(a,b)=1
               ba(i-1, j) += ba(i, j) * (1-win (i, j))     i=[0,a]  j=[0,b]

                    prob[a][b][r] = battle (r , 0)  最后r循环把对于a b打仗所有可能值赋给prob

    然后,对输入进行解析,这里用了mask bit来表示两结点的相邻以及敌对关系:
            owned 表示拥有的地,比如 0000010010010000,表示占有4、7、10三块地,初始时只有一块地。
            enemyMask 表示敌对的编号,那0000010010010000 表示 4 7 10 三个地上的是敌人。
            adjacency[i] 表示与i地相邻的土地,0000010010010000 表示4 7 10三地与i地相邻。

    分析程序中有个dp数组(很大),其实就是用来保存搜索中求的解的,唯一的作用是判断dfs (owned,armies)(搜索函数)的owned 和armies是否已经求过了,并返回已有的值。本来我想试着用别的小一点的东西替代它,结果没能找到,所以还是用这个dp数组来记录吧:

// owned表示当前结点处已占有的土地, armies是剩余的士兵
public double dfs( int owned , int armies ) {
        if (dp[owned][armies] >= 0)                    // 判断是否已经计算过
            return dp[owned][armies];
        if (((65535 - owned) & enemyMask) == 0)        // 没有可攻击的enemy
            return dp[owned][armies] = 1.0;
        double best = 0;                            // 最优解初始化
        for (int i = 0; i < tCount; i++)                    // 对每个土地遍历一遍
            //  (owned & (1 << i)) == 0 不拥有该土地
            //  (adjacency[i] & owned) > 0 与该土地邻接
            if ((owned & (1 << i)) == 0 && (adjacency[i] & owned) > 0) {
                double d = 0;
                for (int j = 1; j <= armies; j++){        // 累加概率
                    // 不同士兵数攻占该地的概率
                    d += prob[armies][enemy[i]][j] * dfs(owned | (1 << i), j);
                }
                best = Math.max(best, d);            // 求最佳解
            }
        return dp[owned][armies] = best;
    }


Links:

My statistic:
    http://www.topcoder.com/stat?c=coder_room_stats&cr=20862220&rd=9809&rm=247580
   
SRM 288 - Problem Set & Analysis :
    http://www.topcoder.com/tc?module=Static&d1=match_editorials&d2=srm288

PKU 2079:
    http://acm.pku.edu.cn/JudgeOnline/showproblem?problem_id=2079

ShanTou Online Judge 1061:
    http://acm.stu.edu.cn/cgi-bin/problem_cn?id=1061

Triangle Area :
    http://mathworld.wolfram.com/TriangleArea.html

CrossProduct  :
    http://mathworld.wolfram.com/CrossProduct.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值