HDU 3362 (状态压缩DP)

http://acm.hdu.edu.cn/showproblem.php?pid=3362

 

题意:题目给出n(n <= 18)个点的二维坐标,并说明某些点是被固定了的,其余则没固定,要求添加一些边,使得还没被固定的点变成固定的,可见题目中的图形sample。
 
由于n很小,而且固定点的顺序没有限制,所以需要用状态压缩DP。这里要注意两点:
 
·当一个没固定的点和两个固定了的点连接后,该点就被间接固定了(三角形的稳定性质)
·无论是直接固定还是间接固定的点,都可以供以后的点用于固定。
 
因此,对于当前需要固定的点,在已经是固定状态的点中选出两个距离当前点最小的,这就保证了局部最优,从起始状态开始转移,最后判断能否到达最终目标状态就可以了。
 
代码:
// 3218 MS
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define inf 1e9
using namespace std;
 
pair<int, int> point[20];
bool fix[20];
double dp[1<<18];
 
double Distance(int a, int b) {
// 计算两点距离
    int x1 = point[a].first, y1 = point[a].second;
    int x2 = point[b].first, y2 = point[b].second;
    return sqrt(1.0*(x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
 
double Fixed(int n, int state, int cur) {
// 计算当前状态state下,使cur这个点变成fixed所花费的最小代价
    double ans = 0;
    bool mark[20];
    double dist[20];
    for (int i = 0; i < n; ++i) {
    // 检查哪些点是已经fixed的点
        if (((1<<i) & state)) {
            mark[i] = 1;
            dist[i] = Distance(i, cur);
        }
        else
            mark[i] = 0;
    }
    for (int i = 0; i < 2; ++i) {
    // 从已经fixed的点中选择两个最接近当前点的进行连接
        double Min = inf;
        int p = 19;
        for (int j = 0; j < n; ++j) {
            if (mark[j] && dist[j] < Min) {
                Min = dist[j];
                p = j;
            }
        }
        ans += Min;
        mark[p] = 0;
    }
    if (ans >= inf) return -1;
    return ans;
}
 
int main() {
    int n;
    while (cin>>n) {
        if (!n) break;
        int begin = 0, target = 0;
        for (int i = 0; i < n; ++i) {
            cin>>point[i].first>>point[i].second>>fix[i];
            if (fix[i])
                begin += (1<<i);
            target += (1<<i);
            // 顺便计算起始状态、目标状态
        }
        for (int i = 0; i < (1<<n); ++i)
            dp[i] = inf;
        dp[begin] = 0;
 
        for (int i = begin; i < target; ++i) {
        // 状态的遍历
            if (dp[i] == inf) continue;
            for (int j = 0; j < n; ++j) {
            // 每次选择一个还没fixed的点
                if (i & (1<<j)) continue;
                double sum = Fixed(n, i, j);
                if (sum >= 0)
                    dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + sum);
            }
        }
        if (dp[target] == inf)
            printf("No Solution\n");
        else
            printf("%.6lf\n", dp[target]);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值