这个是帮同学做的算法题
这两个问题不是太难,但是属于经典算法的应用,值得品味理解和学习
第二题:
状态设计dp[0][i]表示第i个书签不取时所获得最大点赞累积数,dp[1][i]表示第i个书签取时所获得最大点赞累积数,然而可以轻松写出状态转移方程。而具体选了多少件物品或者说选了哪些书签需要从后往前遍历动态规划矩阵寻找。
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner cin=new Scanner(System.in);
int n=cin.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++)
arr[i]=cin.nextInt();
solve(arr);
}
public static void solve(int[] arr){
int n=arr.length;
int[][] dp=new int[2][n];
dp[1][0]=arr[0];
for(int i=1;i<n;i++){
dp[0][i]=Math.max(dp[1][i-1],dp[0][i-1]);
dp[1][i]=dp[0][i-1]+arr[i];
}
int max=Math.max(dp[0][n-1],dp[1][n-1]),res=max;
int cnt=0;
for(int i=arr.length-1;i>=0;i--){
if(res==dp[0][i])
continue;
else
{
cnt++;
res-=arr[i];
}
}
System.out.println(max+" "+cnt);
}
}
第三题:
要求在固定回合以内能够消灭所有怪兽的单次魔法伤害的最小值,二分是比较容易想到的算法,但是check函数的设计其实还蛮复杂的,主要是模拟过程的注意点比较多。首先要贪心的使用魔法伤害,剩余攻击次数(回合数)一定要每次更新都要检查。当魔法伤害用完时,就只需要比较剩余怪兽生命值总和和剩余攻击次数了。
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
const int N=100010;
int life[N];
bool check(int attackNum,int energy,int damage,priority_queue<int> life,LL sum){
while(life.size())
{
if(energy){ //当含有法力值时,优先使用法力值去消灭当前怪兽,因为要在尽可能少的回合里造成最大伤害
int cur=life.top();
life.pop();
sum-=cur;
if(cur>damage){
int k=min(cur/damage,energy);//使用攻击力为damage时消灭当前怪兽所需要的攻击次数
life.push(cur-k*damage);
if(cur-k*damage!=0) //如果该怪兽不能被完全打死,需要重新加入优先队列中等待后续被攻击
sum+=cur-k*damage;
attackNum-=k;
energy-=k;
}
else{
attackNum--;
energy--;
}
if(attackNum<0) //当攻击次数不够时,一定是不能消灭所有怪物的,当法力值energy为0时,还是有可能在剩下回合里消灭完怪物的
return false;
}
else
{
if(sum>attackNum)
return false;
return true;
}
}
return true;
}
int main(){
int N,T,M;
scanf("%d %d %d",&N,&T,&M);
priority_queue<int> q;
LL sum=0;
for(int i=0;i<N;i++){
scanf("%d",life+i); //读取N个怪兽的生命值
q.push(life[i]); //将这些生命值置入大根堆(STL默认大根堆)
sum+=life[i]; //剩余怪兽的总的生命值
}
int l=0,r=q.top(); //因为要求消灭所有怪物的最小值,法术伤害在最小为0,最大为怪物生命值的最大值的范围中进行二分搜索,
while(l<r){
int mid=(l+r)>>1;
if(check(T,M,mid,q,sum))
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}