第一行输入一个整数
,代表有
组测试数据。
对于每一组测试数据,一行输入
个整数
和
,代表有
个技能,怪物有
点血量。
接下来
行,每一行输入两个数
和
,代表该技能造成的伤害和怪物血量小于等于
的时候该技能会造成双倍伤害
对于每一组数据,输出一行,代表使用的最少技能数量,若无法消灭输出-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
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 "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;
} #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
*/