PKU Campus 2014 F:Balloon(解析几何+枚举/dfs)

PKU Campus 2014 F:Balloon(解析几何+枚举/dfs)
总时间限制: 1000ms 内存限制: 65536kB

描述
As you may know, balloons are handed out during ACM/ICPC contests to teams when they solve problems. Inspired by the floating balloons, Albert invents a 3-dimension shooting balloons game. In this game, each balloon is regarded as a single point locating in a 3-dimension position. The player shoots the balloons for several times until all balloons have been eliminated. For each shoot, the player chooses one straight line and all balloons on this line (pay attention here: not a ray but the whole straight line) will be eliminated. You should eliminate at least one balloon in a single shoot. A balloon will be given a score in each shoot. The player will earn the scores from the eliminated balloons. The goal is to eliminate all the balloons and earn as much score as possible within at most t shoots. You can assume that different balloons cannot share the same coordinate.

输入
The first line contains an integer T (1 ≤ T ≤ 20) – the number of test cases.

For each test case:
The first line contains two integers n, t, indicate the number of balloons and the upper limit of number of shoots. 5 ≤ n ≤ 10, 1 ≤ t ≤ 4.
Then follows n lines, each line contains three integers: x, y, z, describe the coordinate (x, y, z) of a balloon. 0 ≤ | x |, | y |, | z | ≤ 5 000.
Then follows n lines, each line contains t integers. The j-th integer on the i-th line is sij, which represents the score of the i-th balloon in the j-th shoot. 0 ≤ sij ≤ 10.

输出

For each test case, output one integer in a single line – the maximum score after all the balloons are eliminated, or “-1” if you cannot eliminate all the balloons.

样例输入

1
6 2
5 -5 2
-2 -1 -2
1 5 -5
-19 55 -40
-9 3 -6
-119 305 -215
0 0
0 0
1 0
1 2
0 3
1 0

样例输出

6


本题的数据量很小,而且由于价值没有一定的规律,不存在什么好的算法,所以只能枚举。关键在于如何写出容易调试的优雅的代码。

枚举关键在于不重(少重)不漏不多,首先,注意到一枪可以打1个,2个及以上,那么在枚举每一枪时候有两种情况:1.只打到一个气球,2.打到两个气球,并且包括这条直线上的所有气球。枚举采用dfs框架,记录气球剩余几个以及得分情况之类的信息。

这个框架下的下面,代码就可以写得很优雅了,事实证明,我的代码没有刻意压缩过,也可以在80余人AC代码中排前十短了。

但是有一个小问题,就是三点共线的代码一开始写错了,以下是点i,j,k三点共线的正确代码(仅使用于整数不考虑误差)。

bool isLine(int i, int j, int k)
{
    LL x1 = x[i] - x[k], y1 = y[i] - y[k], z1 = z[i] - z[k];
    LL x2 = x[j] - x[k], y2 = y[j] - y[k], z2 = z[j] - z[k];
    LL delta1 = x1 * y2 - x2 * y1;
    LL delta2 = y1 * z2 - z1 * y2; 
    LL delta3 = x1 * z2 - z1 * x2;
    return (delta1 == 0) && (delta2 == 0) && (delta3 == 0);
}

因为显然

线
(x1,y1,z1)//(x2,y2,z2)
x1y1=x2y2=x3y3(00=)

那么在添加 00=xy=abxb=ya ,可是这个代数系统中=不是等价类!因为 1=00,2=00

那么一开始很容易把最后一个条件转化成代码里的(delta1 == 0) && (delta2 == 0),因为误以为(*)中两个等号成立这个算式就成立了,可是由于=不再是等价符号了,所以需要三个等号成立,即任意两两等号成立。所以改为(delta1 == 0) && (delta2 == 0) 或者干脆采用算法如果全是0/0则共线,否则拿一组不是0/0解出向量比k,再一一验证。

注意这个小细节,看来我的思维不够缜密,如果没有测试数据,几乎发现不了这个错误。


Accepted    128kB   44ms    1723 B  G++
#include<stdio.h>

typedef long long LL;

const int N = 10;
const int T = 4; 

int cases, n, t;
int x[N], y[N], z[N];
int score[N][T];
bool shoot[N];
int ans;

bool isLine(int i, int j, int k)
{
    LL x1 = x[i] - x[k], y1 = y[i] - y[k], z1 = z[i] - z[k];
    LL x2 = x[j] - x[k], y2 = y[j] - y[k], z2 = z[j] - z[k];
    LL delta1 = x1 * y2 - x2 * y1;
    LL delta2 = y1 * z2 - z1 * y2; 
    LL delta3 = x1 * z2 - z1 * x2;
    return (delta1 == 0) && (delta2 == 0) && (delta3 == 0);
}

void dfs(int now, int left, int total)
{
    bool line[N];
    int sum, num;
    if (left == 0)
    {
        if ((total > ans) or (ans == -1))
            ans = total;
        return;
    }
    if (now == t)
        return;
    //shoot
    for (int i = 0; i < n; i++)
        if (!shoot[i])
        {
            //one balloon this shoot
            shoot[i] = true;
            dfs(now + 1, left - 1, total + score[i][now]);
            shoot[i] = false;

            //more balloons this shoot
            for (int j = i + 1; j < n; j++)
                if (!shoot[j])
                {
                    for (int k = 0; k < n; k++)
                        line[k] = false;
                    sum = num = 0;
                    for (int k = 0; k < n; k++)
                        if ((!shoot[k]) && isLine(i, j, k))
                        { 
                            line[k] = true;
                            num++;
                            sum += score[k][now];
                            shoot[k] = true;                    
                        }
                    dfs(now + 1, left - num, total + sum);
                    for (int k = 0; k < n; k++)
                        if (line[k])
                            shoot[k] = false;
                }
        }
    return;
}

int main()
{
    //freopen("input.txt", "r", stdin);
    scanf("%d", &cases);
    while (cases--)
    {
        scanf("%d%d", &n, &t);
        for (int i = 0; i < n; i++)
            scanf("%d%d%d", &x[i], &y[i], &z[i]);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < t; j++)
                scanf("%d", &score[i][j]);
        for (int i = 0; i < n; i++)
            shoot[i] = false;
        ans = -1;
        dfs(0, n, 0);
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值