http://acm.timus.ru/problem.aspx?space=1&num=1152
有n个阳台围城一圈,每个阳台都有若干个怪兽,一次可以打三个相邻的阳台上的怪兽,它们就会全部死去,但攻击者会受到没有死去怪兽的攻击,每个怪兽的攻击是1unit,问最后攻击者受到的最小伤害。
n <= 20,可以直接dfs过去。
1次WA,1次TLE。
WA是没看透题意,我判断的递归终止的条件是怪兽数目小于等于3,这是不对的,就算怪兽数目小于等于3,也不一定能一次打完,因为它只能打连续的怪兽,若两个怪兽之间的距离大于等于2,那么还需要一次才能打完。
TLE因为没加剪枝,dfs的过程中一旦发现已受攻击值大于最优值,就返回。
#include <stdio.h> #include <iostream> #include <map> #include <set> #include <list> #include <stack> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #include <algorithm> #define LL __int64 #define eps 1e-12 #define PI acos(-1.0) #define PP pair<LL,LL> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1000; const int mod = 1000000009; int a[22],vis[22]; int Min; int n; void dfs(int num,int sum,int att) { if(att >= Min) //TLE的关键! return; if(num == 1) { Min = min(Min,att); return; } //判断没死的怪兽的位置是否相邻,只有相邻递归才结束 else if(num == 2) { int p[3],cnt=0; for(int i = 1; i <= n; i++) if(!vis[i]) p[++cnt] = i; if(p[2] - p[1] <= 1 || p[2]-p[1] == n-1) { Min = min(Min,att); return; } } else if(num == 3) { int p[3],cnt = 0; for(int i = 1; i <= n; i++) { if(!vis[i]) { if(i <= n-2 && !vis[i+1] && !vis[i+2]) { Min = min(Min,att); return; } else if(!vis[n-1] && !vis[n] && !vis[1]) { Min = min(Min,att); return; } } } } for(int i = 1; i <= n; i++) { if(!vis[i]) { int tmp = 0,f1 = 0,f2 = 0,t; vis[i] = 1; tmp += a[i]; if(i > 1 && !vis[i-1]) { vis[i-1] = 1; tmp += a[i-1]; f1 = 1; } else if(i == 1 && !vis[n]) { vis[n] = 1; tmp += a[n]; f1 = 1; } if(i < n && !vis[i+1]) { vis[i+1] = 1; tmp += a[i+1]; f2 = 1; } else if(i == n && !vis[1]) { vis[1] = 1; tmp += a[1]; f2 = 1; } t = sum - tmp; dfs(num-1-f1-f2,t,att+t); vis[i] = 0; if(i > 1 && f1 == 1) vis[i-1] = 0; else if(i == 1 && f1 == 1) vis[n] = 0; if(i < n && f2 == 1) vis[i+1] = 0; else if(i == n && f2 == 1) vis[1] = 0; } } } int main() { int sum; while(~scanf("%d",&n)) { sum = 0; for(int i = 1; i <= n; i++) { scanf("%d",&a[i]); sum += a[i]; } Min = INF; memset(vis,0,sizeof(vis)); dfs(n,sum,0); printf("%d\n",Min); } return 0; }
又换了一种新姿势,因为n<=20,且每个怪物在一种状态下只有0和1两个选择,所以共有(1<<n)-1种状态,然后记忆化搜索。
#include <stdio.h> #include <iostream> #include <map> #include <set> #include <list> #include <stack> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #include <algorithm> #define LL __int64 #define eps 1e-12 #define PI acos(-1.0) using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 50010; int n,num[22]; int dp[(1<<20)+10]; int dfs(int sta) { if(dp[sta] != INF) return dp[sta]; int ss,a,b; for(int i = 1; i <= n; i++) { if(sta&(1<<(i-1))) { ss = sta; a = (i == 1?n:i-1); b = (i == n?1:i+1); ss -= (1<<(i-1)); if(ss&(1<<(a-1))) ss -= (1<<(a-1)); if(ss&(1<<(b-1))) ss -= (1<<(b-1)); int tmp = 0; for(int j = 1; j <= n; j++) if(ss & (1<<(j-1)) ) tmp += num[j]; dp[sta] = min(dp[sta],dfs(ss)+tmp); } } return dp[sta]; } int main() { while(~scanf("%d",&n)) { for(int i = 1; i <= n; i++) scanf("%d",&num[i]); memset(dp,INF,sizeof(dp)); dp[0] = 0; printf("%d\n",dfs((1<<n)-1)); } return 0; }