链接: URAL 1152
题意:有 n 个阳台(暂且这么看吧...)围成一个圆把英雄包围在中央,每个阳台有一定数量的怪物。英雄每开一炮,能摧毁相邻的 3 个阳台,使阳台上面的怪物全部灭亡,但其他阳台里每个怪物会对英雄造成 1 点伤害(强制扣血啊),英雄再发出一炮,剩下的怪物再反击一次,英雄再开一炮,剩下的怪物继续反击一次。。。直到所有怪物被消灭,问英雄最少受到多少伤害。注意,第 1 个阳台和第 n 个阳台相邻,第 2 个阳台摧毁后,第 1 个阳台和第 3 个阳台还是不相邻(只要 n 不等于 3)。
思路:状态 DP + 记忆化搜索。第一次写,学习了。
位操作:
判断点 j 是否属于集合 i :i & (1<<j)
在集合 i 中去除点 j :i – (1<<j) 或者 i & ( ~( 1 << j ) )
在集合 i 中加入点 j :i | (1<<j)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 22
#define status 1<<21
#define INF 0x3f3f3f3f
int n,cost[maxn],dp[status];
void init()
{
memset(dp,-1,sizeof(dp));
dp[0] = 0;
scanf("%d",&n);
for(int i = 0;i < n;++i)
cin>>cost[i];
}
//计算该状态下所剩余的怪物
int value(int state)
{
int sum = 0;
for(int i = 0;i < n;++i)
{
//判断 第 i 个阳台是否还存在
if(state & (1<<i))
{
sum += cost[i];
}
}
return sum;
}
int solve(int state)
{
if(dp[state] != -1) return dp[state];
int cur = INF,next;
for(int i = 0;i < n;++i)
{
//判断第 i 个阳台是否存在
if(state & (1<<i))
{
next = state;
//打掉该阳台的相邻3个阳台进入下个状态
next &= ~(1<<((i-1+n)%n));
next &= ~(1<<i);
next &= ~(1<<((i+1)%n));
//这一步所受伤害
cur = min(cur,value(next) + solve(next));
}
}
//把最小伤害存到 dp[state]
dp[state] = cur;
return cur;
}
int main()
{
init();
int ans = solve((1<<n) - 1);
cout<<ans<<endl;
return 0;
}