第一行输入一个整数
,代表有
组测试数据。
对于每一组测试数据,一行输入
个整数
和
,代表有
个技能,怪物有
点血量。
接下来
行,每一行输入两个数
和
,代表该技能造成的伤害和怪物血量小于等于
的时候该技能会造成双倍伤害
对于每一组数据,输出一行,代表使用的最少技能数量,若无法消灭输出-1。
3 3 100 10 20 45 89 5 40 3 100 10 20 45 90 5 40 3 100 10 20 45 84 5 40
3 2 -1
总共3组数据对于第一组:我们首先用技能1,此时怪物血量剩余90,然后使用技能3,此时怪物剩余血量为85,最后用技能2,由于技能2在怪物血量小于89的时候双倍伤害,故此时怪物已经消灭,答案为3对于第二组:我们首先用技能1,此时怪物血量剩余90,然后用技能2,由于技能2在怪物血量小于90的时候双倍伤害,故此时怪物已经消灭,答案为2对于第三组:我们首先用技能1,此时怪物血量剩余90,然后使用技能3,此时怪物剩余血量为85,最后用技能2,由于技能2在怪物血量小于84的时候双倍伤害,故此时怪物无法消灭,输出-1
#include "bits/stdc++.h" using namespace std; int ans=INT_MAX; vector<vector<int>> Ax; void backtracking(int step,int Hp){ if(Hp<=0){ ans=min(ans,step); return ; } for(int i=0;i<Ax.size();++i){ if(Ax[i][2]){ Ax[i][2]=0; //使用过了,为0 if(Hp<=Ax[i][1]){ backtracking(step+1, Hp-2*Ax[i][0]); } else{ backtracking(step+1, Hp-Ax[i][0]); } Ax[i][2]=1; //回溯,恢复未使用状态 } } } int main() { int T; cin >> T ; while (T) { // 注意 while 处理多个 case int n,m; cin >>n>>m; if(m==0){ cout<<0<<endl; break; } // 分别为每个技能对应的A,x以及使用标记1 Ax=vector<vector<int>>(n,vector<int>(3,1)); for(int i=0;i<n;++i){ cin>>Ax[i][0]>>Ax[i][1]; } backtracking(0,m); if(ans>n)cout<<-1<<endl; else cout<<ans<<endl; T--; ans=INT_MAX; } return 0; }
t = int(input()) for _ in range(t): #n个技能,m点血量 n,m = list(map(int,input().split())) tem = [] #存储每个技能的伤害 #tem.sort(key=lambda x:(-x[1],-x[0])) for _ in range(n): tem.append(tuple(map(int,input().split()))) length = len(tem) ans = -1 def dfs(blood,visit=set()): global ans #假设剩余技能全部打出二倍伤害 rest_max_sum = sum([2*tem[i][0] for i in range(length) if i not in visit]) if blood > rest_max_sum: return #剪枝 if ans != -1 and len(visit) >= ans: return #剪枝 if len(visit) == length and blood > 0: return if blood <= 0: ans = min(ans,len(visit)) if ans >= 0 else len(visit) for ind,(x,y) in enumerate(tem): if ind not in visit: visit.add(ind) if blood <= y: dfs(blood-2*x,visit) else: dfs(blood-x,visit) visit.remove(ind) dfs(m) print(ans)
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 15; int n, m; int a[N], b[N]; bool st[N]; int res = N; void dfs(int u, int hp, int cnt) { if(hp <= 0) { res = min(res, cnt); return ; } if(u == n + 1) return ; for(int i = 1; i <= n; i ++) { if(!st[i]) { st[i] = true; if(hp <= b[i]) dfs(u + 1, hp - a[i] * 2, cnt + 1); else dfs(u + 1, hp - a[i], cnt + 1); st[i] = false; } } } int main() { int T; cin >> T; while(T --) { cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> a[i] >> b[i]; res = N; memset(st, false, sizeof st); dfs(0, m, 0); if(res == N) puts("-1"); else cout << res << endl; } return 0; }
#include<iostream> using namespace std; const int N = 11,M = 1e6 + 3; int kill[N],blood[M]; bool used[N]; // n:技能个数 st:当前来到第st rest:怪兽目前剩余血量 int f(int n,int st,int rest,bool used[]) { //monster死了 用了st-1个技能 if(rest <= 0) return st - 1; //技能用完了 monster没死 返回最大值 代表无效 if(st == n + 1) return 0x3f3f3f3f; int ans = 0x3f3f3f3f; for(int i = 1; i <= n; ++ i) { if(used[i] == true) continue; used[i] = true; ans = min(ans,f(n,st + 1,rest-(rest > blood[i] ? kill[i] : kill[i] * 2),used)); used[i] = false; } return ans; } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int _;cin >> _; while(_ --) { int n,m;cin >> n >> m; for(int i = 1;i <= n; ++ i) { int k,b;cin >> k >> b; kill[i] = k; blood[i] = b; } for(int i = 1;i <= N; ++ i) used[i] = false; int ans = f(n,1,m,used); if(ans == 0x3f3f3f3f) cout << -1 << '\n'; else cout << ans << '\n'; } return 0; }
dfs(health,m)
代表剩下生命值是health,剩下技能集合是m情况下,把怪物杀死所用技能最少的数量。因为有@cache
缓存状态,所以不会重复计算,很快。
初始集合u=(1<<n)-1
(全1)
from math import inf from functools import cache def minSkill(l: list, m: int) -> int: n = len(l) u = (1 << n) - 1 @cache def dfs(health: int, ss: int) -> int: if health <= 0: return 0 if ss == 0: return inf ans = inf for i in range(n): if (ss >> i) & 1: A, x = l[i] if health <= x: ans = min(ans, dfs(health - 2 * A, ss ^ (1 << i)) + 1) else: ans = min(ans, dfs(health - A, ss ^ (1 << i)) + 1) return ans t = dfs(m, u) return t if t != inf else -1 T = int(input()) for _ in range(T): n, m = map(int, input().split()) l = [] for _ in range(n): A, x = map(int, input().split()) l.append([A, x]) print(minSkill(l, m))
// 暴力回溯法 #include <climits> #include <iostream> #include <utility> #include <vector> #include "bits/stdc++.h" using namespace std; int imin = INT_MAX; int blood; bool flag; void recur(vector<pair<int, int>>& arr, int index) { if(index == arr.size() - 1) { // 最小技能次数 int tmp = blood; int count = 0; for(int i = 0; i < arr.size(); i++) { if(tmp <= arr[i].second) { tmp -= arr[i].first * 2; }else { tmp -= arr[i].first; } count++; if(tmp <= 0) { flag = true; imin = min(count, imin); break; } } } for(int i = index; i < arr.size(); i++) { swap(arr[index], arr[i]); recur(arr, index+1); swap(arr[index], arr[i]); } } int main() { int n; cin >> n; while (n--) { int a, b; int t1, t2; cin >> a >> blood; vector<pair<int, int>> arr; for (int i = 0; i < a; i++) { cin >> t1 >> t2; arr.emplace_back(make_pair(t1, t2)); } flag = false; recur(arr, 0); cout << ((flag == true) ? imin : -1) << endl; imin = INT_MAX; } return 0; }
import java.util.ArrayList; import java.util.List; import java.util.Scanner; /** * @author qxm * @create 2023-02-27 22:12 **/ //回溯问题的排列问题 public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); for (int i = 0; i < t; i++) { int n = sc.nextInt(); int m = sc.nextInt(); int[][] jn = new int[n][2]; for (int j = 0; j < n; j++) { jn[j][0] = sc.nextInt(); jn[j][1] = sc.nextInt(); } Main obj = new Main(); obj.blood = m; boolean[] isUsed = new boolean[n]; obj.backtracking(jn, isUsed); if (obj.min == Integer.MAX_VALUE) { System.out.println(-1); } else { System.out.println(obj.min); } } } int min = Integer.MAX_VALUE; List<Integer> path = new ArrayList<>(); int blood = 0; public void backtracking(int[][] jn, boolean[] isUsed) { if (blood <= 0) { min = Math.min(min, path.size()); return; } for (int i = 0; i < jn.length; i++) { if (isUsed[i]) { continue; } if (blood <= jn[i][1]) { blood -= jn[i][0] * 2; path.add(jn[i][0]); isUsed[i] = true; backtracking(jn, isUsed); isUsed[i] = false; path.remove(path.size() - 1); blood += jn[i][0] * 2; } else { blood -= jn[i][0]; path.add(jn[i][0]); isUsed[i] = true; backtracking(jn, isUsed); isUsed[i] = false; path.remove(path.size() - 1); blood += jn[i][0]; } } } }
import java.util.Scanner; import java.util.Stack; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int size = in.nextInt();// 用例数 int index = 0; while (in.hasNextInt()) { int skillNum = in.nextInt(); int blood = in.nextInt(); int[][] skills = new int[skillNum][2]; for (int i = 0; i < skillNum; i++) { skills[i][0] = in.nextInt(); skills[i][1] = in.nextInt(); } minKilled(skills, blood); } } public static void minKilled(int[][] skills, int blood) { // skills[i] :技能伤害 小于特定值则造成双倍伤害 int ans = Integer.MAX_VALUE; for (int i = 0; i < skills.length; i++) { boolean[] used = new boolean[skills.length]; used[i] = true; ans = Math.min(ans, dfs(skills, i, blood, used, 0)); used[i] = false; } System.out.println(ans == Integer.MAX_VALUE ? -1 : ans); } private static int dfs(int[][] skills, int pos, int restBlood, boolean[] used, int usedSkillNum) { if (restBlood <= 0) { return 0; } if (usedSkillNum > skills.length) { return Integer.MAX_VALUE; } int ans = Integer.MAX_VALUE; int rest = restBlood - (restBlood <= skills[pos][1] ? 2 * skills[pos][0] : skills[pos][0]); if (rest <= 0) { return 1; } for (int i = 0; i < skills.length; i++) { if (i != pos && !used[i]) { used[i] = true; int dfs = dfs(skills, i, rest, used, usedSkillNum + 1); if (dfs != Integer.MAX_VALUE) { ans = Math.min(ans, 1 + dfs); } used[i] = false; } } return ans; } }
import java.util.Scanner; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static int res; //全局变量,可以在回溯的过程中不考虑返回值 public static void backTracking(int[]axe, int[] cmp, int state, int m, int step) { if (m <= 0) { //血量小于0收割结果 res = Math.min(step, res); return; } if (state == 0) return; for (int i = 0; i < axe.length; i++) { if (((state>>i) & 1) != 1) continue; int t = m - axe[i]; if (m <= cmp[i]) t = t - axe[i]; //继续向下递归 backTracking(axe, cmp, state^(1<<i), t, step+1); } return; } public static void main(String[] args) { Scanner in = new Scanner(System.in); // 注意 hasNext 和 hasNextLine 的区别 int T = in.nextInt(); for (int i = 0; i < T; i++) { res = Integer.MAX_VALUE; int n = in.nextInt(); int m = in.nextInt(); int[] axe = new int[n]; int[] cmp = new int[n]; for (int j = 0; j < n; j++) { axe[j] = in.nextInt(); cmp[j] = in.nextInt(); } int state = (1<<(n)) - 1; backTracking(axe, cmp, state, m, 0); if (res == Integer.MAX_VALUE) res = -1; System.out.println(res); } } }用一个状态数组标记已经访问过的state可能耗时会缩短一些。
#include <iostream> #include <vector> #include <map> #include <numeric> void backtrace(const std::vector<std::pair<int, int>>& ax, std::vector<int>& recorder, int T_i, std::vector<int>& status) { if (T_i <=0) { status.push_back(std::accumulate(recorder.begin(), recorder.end(), 0)); return; } for (size_t i=0; i<ax.size(); i++) { if (!recorder[i]) { recorder[i] = 1; // 选择 // if (T_i<=ax[i].second) { // 双倍伤害 // T_i -= 2*ax[i].first; // } else { // T_i -= ax[i].first; // 直接伤害 // } // backtrace(ax, recorder, T_i, status); // recorder[i] = 0; //撤销选择 // if (T_i<=ax[i].second) { // 双倍伤害 ***此时Ti变了,会导致判断条件发生变化,应该用临时变量代替输入到backtrace里面,或者直接传入结果。 // T_i += 2*ax[i].first; // } else { // T_i += ax[i].first; // 直接伤害 // } // 此时 递推回来后T_i没变 if (T_i <= ax[i].second) { backtrace(ax, recorder, T_i-2*ax[i].first, status); } else { backtrace(ax, recorder, T_i-ax[i].first, status); } recorder[i] = 0; // 撤销选择 } } // status.push_back(11); // 最多只有10个技能 } int main(int argc, const char** argv) { int T; std::cin >> T; // 测试次数 int m, n; int A,x; std::vector<std::pair<int, int>> ax; std::vector<int> num; // 每次的最小攻击次数 for (int i=0; i<T; i++) { std::cin>>n >> m; // 技能,血量 while(n--) { std::cin>>A>>x; ax.push_back({A, x}); // 伤害, 阈值 } // std::sort(ax.begin(), ax.end(), [](std::pair<int, int> a , std::pair<int, int> b){ // return a.second<=b.second; // }); std::vector<int> recorder(ax.size(), 0); // 单次记录是否选择 std::vector<int> status; // 单次所有的状态 status.push_back(11); //最多只有10个技能 backtrace(ax, recorder, m, status); int min_n = 11; //最小进攻次数 for (size_t s=0; s < status.size(); s++) { if (min_n > status[s]) { min_n = status[s]; } } if (min_n==11) num.push_back(-1); else num.push_back(min_n); ax.clear(); } for (size_t i=0; i<num.size(); i++) { std::cout << num[i] << std::endl; } return 0; } /* 3 3 100 10 20 45 89 5 40 3 100 10 20 45 90 5 40 3 100 10 20 45 84 5 40 */