uva 11008 Antimatter Ray Clearcutting 记忆化搜索

题目:

It's year 2465, and you are the Chief Engineer for Glori ed Lumberjacks Inc. on planet Trie. There is a number of trees

 that you need to cut down, and the only weapon you have is a high-powered antimatter ray that will cut through trees like butter. Fuel cells for the antimatter ray are very expensive, so your 

strategy is: stand somewhere in the forest and shoot the ray in some chosen direction. This will cut down all the trees

 that lie on the line in that direction. Given the locations of several trees and the number of trees that you are required

 to cut, what is the minimum number of shots that you need to re?

 

Input

 

The rst line of input gives the number of cases, N (at most 20). N test cases follow. Each one starts with 2 lines containing the integers n 

(the number of trees in the forest, at most 16) and m (the number of trees you need to cut, at most n). The next n lines will each give the

 (x,y) coordinates of a tree (integers in the range [ 1000; 1000]).

 

Output

 

For each test case, output the line `Case #x:', where x is the number of the test case. On the next line, print the number of antimatter ray 

shots required to cut down at least m trees. Print an empty line between test cases.

 

Notes:

 

In the rst test case, you can cut down 4 trees by standing at (0, -1) and ring north (cutting 2 trees) and then standing at (1, -1) and again ring north (

cutting 2 more trees).

 

In the second test case, you should stand at (3,-1) and re north (cutting 4 trees) and then stand at (-1, -1) and re north-east (cutting 3

 more trees).

 

Sample Input

 

2

 

4

 

4

 

0 0

 

0 1

 

1 0

 

1 1

 

9

 

7

 

0 0

 

1 1

 

0 2

 

2 0

 

2 2

 

3 0

 

3 1

 

3 2

 

3 4

 

Sample Output

 

Case #1: 2

 

Case #2: 2

题目大意:

给n个点,求每次能去掉一条直线的点,求去掉m个需要多少次。

题目思路:

 

1、因为上次的决定会影响到后面的判断,所以不能用贪心

2、只给最多18个点,所以可以用搜索的方式

3、因为最多18个状态,可以压缩成一个,使得可以记忆,减少搜索数量

4、.尽量减枝减少时间

程序:

 

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
bool vv[20];//记录是否是之前的直线
int m,beg,da;
int v[10000000];//记录去掉点的状态
struct node
{
    int x,y;
} a[20];
int dp(int tree,int ans);//搜索
int line(int aa,int bb,int cc);//判断是否是直线
int main()
{
    int ci;
    scanf("%d",&ci);
    for(int k=1; k<=ci; k++)
    {
        da=inf;
        memset(v,inf,sizeof(v));
        scanf("%d%d",&m,&beg);
        for(int i=0; i<m; i++)
            scanf("%d%d",&a[i].x,&a[i].y);
        printf("Case #%d:\n%d\n",k,dp(0,0));
        if(k!=ci) printf("\n");
    }
    return 0;
}
int dp(int tree,int ans)
{
    if(ans>=da)//减枝1  只搜索比保存结果小的
        return ans;
    memset(vv,0,sizeof(vv));
    if(v[tree]!=inf)//减枝2 记忆化搜索
        return v[tree];
    int w=0;
    for(int tt=tree; tt>0; tt>>=1)
        w+=tt&1;//判断数量
    if(w>=beg)
        return da=ans;
    for(int i=0; i<m; i++)
        if(!((tree>>i)&1)&&!vv[i])//判断是否倍去点或搜过
            for(int j=i+1; j<m; j++)
                if(!((tree>>j)&1)&&!vv[j])//同上
                {
                    vv[i]=vv[j]=1;
                    int tt=tree;
                    tt+=(1<<i)+(1<<j);
                    for(int k=j+1; k<m; k++)//搜索直线上的所有点
                        if(!((tt>>k)&1))//可以吧 j+1去掉变成直线
                            if(!vv[k]&&line(i,j,k))
                            {
                                tt=tt+(1<<k);
                                vv[k]=1;
                            }
                    v[tt]=dp(tt,ans+1);
                    v[tree]=min(v[tree],v[tt]);
                    if(v[tree]==ans+1)
                        return v[tree];
                }
    if(v[tree]==inf)
        return ans+1;
    return v[tree];
}
int line(int aa,int bb,int cc)
{
    int x1=a[bb].x-a[aa].x;
    int y1=a[bb].y-a[aa].y;
    int x2=a[cc].x-a[aa].x;
    int y2=a[cc].y-a[aa].y;
    return x1*y2-x2*y1==0?1:0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值