POJ3920状态压缩和组合数学

本文介绍了一个关于激光射击路径优化的问题,通过状态压缩算法降低复杂度,实现最少能量消耗的策略。考虑敌人位置与射击顺序的选择,确保每个敌人都只被攻击一次。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

Acmers have been the Earth Protector against the evil enemy for a long time, now it’s your turn to protect our home.
There are 2 * n enemies in the map. Your task is to clear all of them with your super laser gun at the fixed position (x, y).
For each laser shot, your laser beam can reflect 1 times (must be 1 times), which means it can kill 2 enemies at one time. And the energy this shot costs is the total length of the laser path.
For example, if you are at (0, 0), and use one laser shot kills the 2 enemies in the order of (3, 4), (6, 0), then the energy this shot costs is 5.0 + 5.0 = 10. 00.
Since there are 2 * n enemies, you have to shot n times to clear all of them. For each shot, it is you that select two existed enemies and decide the reflect order.
Now, telling you your position and the 2n enemies’ position, to save the energy, can you tell me how much energy you need at least to clear all of them?
Note that:

Each enemy can only be attacked once.
All the positions will be unique.
You must attack 2 different enemies in one shot.
You can’t change your position.

算法思路

1.这一题使用状态压缩是很显然的。
2.我们把解题的步骤分为n步,每一步挑选出两个目标进行射击。我们假设这两个目标为Ai,Aj,那么,我们是否要尝试每一个i,j呢?
3.答案是不需要的,因为我们在选择射击目标的时候,其实选择出来的组合又构成了一个组合关系,比如第一次选择的两个目标完全可以和第二次选择的两个目标调换。因为它们没有先后的关系。
4.所以,知道了这一点之后,我们的算法复杂度降为O(n2)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;

#define MAXN 11
#define MX 1050000
#define MAX 1e+20

struct CPoint{
    double x,y;
}PointList[2*MAXN],st;

int n,cas = 0;
int t;
double dp[MX];

double dis(CPoint s,CPoint t)
{
    return sqrt((s.x-t.x)*(s.x-t.x)+(s.y-t.y)*(s.y-t.y));
}

double Dfs(int position)
{
    //printf("%d\n",position);
    if(dp[position] >= 0)return dp[position];
    dp[position] = MAX;
    int i,j;

    //select two targets
    for(i=0;i<=2*n-1;i++){
        //printf("%d %d %d\n",position,i,n);
        if(position&(1<<i)){
            //printf("YES\n");
            for(j=i+1;j<=2*n-1;j++){
                if(position & (1<<j)){
                    double dist = dis(PointList[i],PointList[j])+min(dis(st,PointList[i]),dis(st,PointList[j]));
                    dp[position] = min(dp[position],Dfs(position-(1<<i)-(1<<j))+dist);
                }
            }
            //no need to try all the combinations
            break;
        }
    }

    //printf("%d\n",position);
    return dp[position];
}

int main()
{
    //freopen("input","r",stdin);
    int i;
    scanf("%d",&t);

    while(t--){
        cas++;
        scanf("%lf %lf",&st.x,&st.y);
        scanf("%d",&n);
        for(i=0;i<2*n;i++)
            scanf("%lf %lf",&PointList[i].x,&PointList[i].y);

        for(i=0;i<=(1<<(2*n));i++)
            dp[i] = -1.0;
        dp[0] = 0;

        printf("Case #%d: ",cas);

        //solve the problem from the top
        printf("%.2f\n",Dfs((1<<(2*n))-1));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值