UVa 11008 Antimatter Ray Clearcutting(记忆化搜索)

题意:

n个坐标上面分别有n棵树,每发射一次枪,可以把一条直线上面的树都消灭掉。

问要消灭到m棵树,最少需要几枪。

思路:

记忆化搜索。原则上每枪最少要消灭2棵树,如果最后一枪下去导致剩下的树小于等于(n-m),则控制最后一枪使结果恰好等于结果。其实发射枪的次数还是不变的。

用一个整数的位表示剩下的树,预处理的过程中,g[i, j]表示与i树和j树在一条直线上面的树。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;

const int MAXN = 20;
const int MAXD = 65600;
int f[MAXD];
int x[MAXN], y[MAXN];
int g[MAXN][MAXN];
int n, m, d;

void init()
{
    memset(g, 0, sizeof(g));

    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            if (i != j)
                for (int k = n - 1; k >= 0; --k)
                {
                    g[i][j] <<= 1;
                    if((y[j] - y[i]) * (x[k] - x[i]) == (x[j] - x[i]) * (y[k] - y[i]))
                        ++g[i][j];
                }
}

int dp(int u)
{
    if (f[u] != -1)
        return f[u];

    int c = 0;
    for (int i = 0; i < n; ++i)
        if (u & (1<<i))
            ++c;
    if (c <= d)
        return f[u] = 0;

    if (c == 1)
        return f[u] = 1;

    int ans = INT_MAX;
    for (int i = 0; i < n; ++i)
        if (u & (1<<i))
            for (int j = i + 1; j < n; ++j)
                if (u & (1<<j))
                    ans = min(ans, dp(u & (~g[i][j])) + 1);

    return f[u] = ans;
}

int main()
{
    int cases, count = 0;
    scanf("%d", &cases);
    while (cases--)
    {
        scanf("%d %d", &n, &m);
        for (int i = 0; i < n; ++i)
            scanf("%d %d", &x[i], &y[i]);

        init();

        memset(f, -1, sizeof(f));
        d = n - m;
        int ans = dp((1<<n) - 1);

        printf("Case #%d:\n", ++count);
        printf("%d\n", ans);
        if (cases)
            printf("\n");
    }
    return 0;
}

 

 

posted @ 2012-11-25 17:01  kedebug  阅读(317)  评论(0编辑  收藏  举报