HDU 3920Clear All of Them I(状压DP)

时间:2021-03-01 05:55:39

HDU 3920   Clear All of Them I

题目是说有2n个敌人,现在可以发n枚炮弹,每枚炮弹可以(可以且仅可以)打两个敌人,每一枚炮弹的花费等于它所行进的距离,现在要消灭所有的敌人,问最少花费是多少(反正题意大概就是这样啦,知道怎么回事就好了,解释不清了)

一看到n<=10而且又是在DP专题里的,就知道这个显然是状压DP,由于有2n个敌人,所以状态表示由当前状态再打两个人来转移,

10000100表示打了这两个敌人的最小花费

所以状态的转移是由前往后递推的,假如当前状态是cur,上一个状态是last,应该满足存在i!=j有last&(1<<i)=0且last&(1<<j)=0且last | (1<<i)|(1<<j) = cur,由此来更新当前的状态cur,最后的答案就是DP[(1<<(2n)) - 1]

注意到上面我们是要枚举i和j的,所以这个的总复杂度就是20 * 20 * 2^20,这显然是会超时的, 所以需要优化

注意到如果存在两队人(a,b)(c,d)我们先打(a, b)再打(c, d)和先打(c,d)在打(a, b)是一样的,所以我们完全可以每次取last中最小的一位是0的与后面所有的0组合,这样不仅没有漏掉解,而且复杂度也将到了O(20 * 2^20)这就可以过了

下面从前往后美剧上一个状态时递推时我是用的队列存的所有状态,其实枚举过去也是可以的

 //#pragma comment(linker,"/STACK:102400000,102400000")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 1e9
#define inf (-((LL)1<<40))
#define lson k<<1, L, mid
#define rson k<<1|1, mid+1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FOPENIN(IN) freopen(IN, "r", stdin)
#define FOPENOUT(OUT) freopen(OUT, "w", stdout)
template<class T> T CMP_MIN(T a, T b) { return a < b; }
template<class T> T CMP_MAX(T a, T b) { return a > b; }
template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; } //typedef __int64 LL;
//typedef long long LL;
const int MAXN = ;
const int MAXM = ;
const double eps = 1e-;
//const LL MOD = 1000000007; int T, N;
typedef double Point[];
Point st, p[MAXN];
double dis[MAXN][MAXN], d[MAXN], dp[<<]; double calc(Point a, Point b)
{
double x = a[] - b[];
double y = a[] - b[];
return sqrt(x*x + y*y);
} void getDis()
{
for(int i=;i<N;i++)
{
d[i] = calc(st, p[i]);
for(int j=i+;j<N;j++)
{
dis[j][i] = dis[i][j] = calc(p[i], p[j]);
}
}
} int main()
{
int t = ;
scanf("%d", &T);
while(T--)
{
scanf("%lf %lf", &st[], &st[]);
scanf("%d", &N);
N <<= ;
for(int i=;i<N;i++)
scanf("%lf %lf", &p[i][], &p[i][]);
getDis();
dp[] = ;
for(int i=;i<(<<N);i++) dp[i] = INF;
queue<int>q;
q.push();
while(!q.empty())
{
int now = q.front(); q.pop();
int f=, r;
while( now & (<<f) && f < N)
f++;
for(r = f + ; r < N; r ++ )
if(!(now & (<<r)))
{
int next = now | (<<f) | (<<r);
double minDis = MIN(d[f], d[r]) + dis[f][r];
if( fabs(dp[next] - INF) < eps )
{
q.push(next);
dp[next] = dp[now] + minDis;
}
else if( dp[now] + minDis < dp[next] )
dp[next] = dp[now] + minDis;
}
}
printf("Case #%d: %.2lf%\n", ++t, dp[(<<N)-]);
}
return ;
}