Fork me on GitHub

2014 Super Training #1 B Fix 状压DP

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

开始准备贪心搞,结果发现太难了,一直都没做出来。后来才知道要用状压DP。

题意:题目给出n(n <= 18)个点的二维坐标,并说明某些点是被固定了的,其余则没固定,要求添加一些边,使得还没被固定的点变成固定的,可见题目中的图形sample。

由于n很小,而且固定点的顺序没有限制,所以需要用状态压缩DP。

注意:
1.当一个没固定的点和两个固定了的点连接后,该点就被(间接)固定了(三角形的稳定性质)
2.被固定的点还可以用来固定别的点

因此,对于当前需要固定的点,在已经是固定状态的点中选出两个距离当前点最小的,这就保证了局部最优,从起始状态开始转移,最后判断能否到达最终目标状态就可以了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Mod 1000000007
using namespace std;
#define N 100007

struct Point
{
    double x,y;
    int f;
}p[22];
double dp[1<<20];
int unfix[21];
double dis[20];
int n;

double Dis(int i,int j)
{
    Point ka = p[i];
    Point kb = p[j];
    return (double)sqrt((ka.x-kb.x)*(ka.x-kb.x)+(ka.y-kb.y)*(ka.y-kb.y));
}

double Fixit(int state,int tofix)
{
    int i = 0,j;
    int tmp = state;
    memset(dis,0,sizeof(dis));
    while(i < n)
    {
        if(tmp & 1)  //已固定点
        {
            unfix[i] = 0;
            dis[i] = Dis(tofix,i);
        }
        else
            unfix[i] = 1;
        i++;
        tmp >>= 1;
    }
    double mini_1 = Mod;
    int tag = -1;
    for(i=0;i<n;i++)   //选第一个点
    {
        if(!unfix[i])  //fixed
        {
            if(dis[i] < mini_1)
            {
                mini_1 = dis[i];
                tag = i;
            }
        }
    }
    double mini_2 = Mod;
    for(i=0;i<n;i++)   //选第二个点
    {
        if(i == tag)   //选过
            continue;
        if(!unfix[i])
        {
            if(dis[i] < mini_2)
                mini_2 = dis[i];
        }
    }
    if(mini_1+mini_2 >= Mod)
        return -1;
    return mini_1+mini_2;
}

int main()
{
    int i,j;
    int Na;
    while(scanf("%d",&n)!=EOF && n)
    {
        int S = 0;
        Na = (1<<n)-1;
        for(i=0;i<n;i++)
        {
            scanf("%lf%lf%d",&p[i].x,&p[i].y,&p[i].f);
            if(p[i].f)
                S |= (1<<i);
        }
        for(i=0;i<=Na;i++)
            dp[i] = Mod;
        dp[S] = 0;
        for(i=S;i<Na;i++)
        {
            if(dp[i] == Mod)
                continue;
            for(j=0;j<n;j++)  //选一个unfix的点固定
            {
                if(i & (1<<j)) //已fix,跳过
                    continue;
                double delta = Fixit(i,j);
                if(delta == -1)
                    continue;
                else  //更新固定完这个点后的最少花费
                    dp[i|(1<<j)] = min(dp[i|(1<<j)],dp[i]+delta);
            }
        }
        if(dp[Na] == Mod)
            puts("No Solution");
        else
            printf("%.6lf\n",dp[Na]);
    }
    return 0;
}
View Code

 

posted @ 2014-06-27 21:38  whatbeg  阅读(389)  评论(0编辑  收藏  举报