牛客练习赛61题解

题目链接

A.打怪

题意:
你 有 h 的 血 量 , a 的 攻 击 你有h的血量,a的攻击 ha
怪 兽 有 H 的 血 量 , A 的 攻 击 怪兽有H的血量,A的攻击 HA
每 次 你 先 出 手 , 轮 流 攻 击 , 血 量 小 于 等 于 0 就 死 每次你先出手,轮流攻击,血量小于等于0就死 0
问 不 死 的 前 提 最 多 能 打 多 少 怪 问不死的前提最多能打多少怪
不 会 死 输 出 − 1 不会死输出-1 1
题解:
首 先 不 会 死 就 说 明 , 每 次 都 能 一 次 解 决 怪 兽 即 a > = H 首先不会死就说明,每次都能一次解决怪兽即a>=H a>=H
然 后 算 怪 兽 每 次 的 攻 击 次 数 , 由 于 你 是 先 手 , 所 以 最 后 一 击 怪 兽 不 会 还 手 然后算怪兽每次的攻击次数,由于你是先手,所以最后一击怪兽不会还手
就 算 还 差 一 次 杀 死 怪 兽 的 次 数 , 乘 上 怪 兽 的 攻 击 就算还差一次杀死怪兽的次数,乘上怪兽的攻击
在 自 己 不 死 的 前 提 看 自 己 能 承 受 多 少 次 , 就 是 杀 死 多 少 只 怪 兽 在自己不死的前提看自己能承受多少次,就是杀死多少只怪兽

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=3e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};




int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int h,a,H,A;
        cin>>h>>a>>H>>A;
        if(a>=H)cout<<-1;
        else {
            int k=(H-1)/a;
            int p=A*k;
            cout<<(h-1)/p;
        }
        cout<<endl;
    }


    return 0;
}


B.吃水果

题意:
有 a 个 苹 果 和 b 个 香 蕉 有a个苹果和b个香蕉 ab
每 天 必 须 同 时 吃 掉 1 个 苹 果 和 1 个 香 蕉 每天必须同时吃掉1个苹果和1个香蕉 11
或 者 你 可 以 使 某 个 水 果 数 量 翻 倍 或者你可以使某个水果数量翻倍 使
问 最 少 多 天 能 让 两 个 水 果 都 没 有 问最少多天能让两个水果都没有
题解:
贪 心 + 模 拟 贪心+模拟 +
先 让 数 量 小 的 每 次 翻 倍 , 直 到 翻 倍 后 比 另 一 个 大 先让数量小的每次翻倍,直到翻倍后比另一个大
然 后 开 始 吃 , 吃 到 又 可 以 翻 倍 的 时 候 继 续 翻 倍 然后开始吃,吃到又可以翻倍的时候继续翻倍
直 到 a = = b 结 束 直到a==b结束 a==b

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=3e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};




int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int a,b;
        cin>>a>>b;
        int ans=0;
        if(a>b)swap(a,b);
        while(1){
            if(a==b){cout<<ans+a<<endl;break;}
            if(a*2<=b)a*=2,ans++;
            if(a*2>b)a--,b--,ans++;
        }
    }


    return 0;
}


C.四个选项

题意:
12 道 选 择 题 , 每 道 答 案 可 能 是 A , B , C , D 12道选择题,每道答案可能是A,B,C,D 12A,B,C,D
要 求 12 道 中 有 a 道 A , b 道 B , c 道 C , d 道 D 要求12道中有a道A,b道B,c道C,d道D 12aAbBcCdD
并 且 给 出 m 个 约 束 条 件 , 每 次 保 证 给 出 的 x 和 y 具 有 相 同 答 案 并且给出m个约束条件,每次保证给出的x和y具有相同答案 mxy
题解:
D F S + 并 查 集 DFS+并查集 DFS+
首 先 用 并 查 集 求 出 有 多 少 个 相 同 答 案 的 集 合 首先用并查集求出有多少个相同答案的集合
然 后 进 行 D F S , 对 每 个 集 合 进 行 赋 予 答 案 , 并 维 护 每 个 答 案 的 状 态 然后进行DFS,对每个集合进行赋予答案,并维护每个答案的状态 DFS
直 到 和 题 目 要 求 相 等 a n s + + 直到和题目要求相等ans++ ans++
剪 枝 : 每 次 每 个 答 案 加 的 集 合 元 素 数 量 不 能 超 过 这 道 题 的 答 案 剪枝:每次每个答案加的集合元素数量不能超过这道题的答案

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=3e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

vector<int> v;
int a,b,c,d,m,n;
int fa[20],cnt[20],ans;
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void dfs(int i,int xa,int xb,int xc,int xd){
    if(xa==a&&xb==b&&xc==c&&xd==d)ans++;
    if(i==n)return;
    if(xa+v[i]<=a)dfs(i+1,xa+v[i],xb,xc,xd);
    if(xb+v[i]<=b)dfs(i+1,xa,xb+v[i],xc,xd);
    if(xc+v[i]<=c)dfs(i+1,xa,xb,xc+v[i],xd);
    if(xd+v[i]<=d)dfs(i+1,xa,xb,xc,xd+v[i]);
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

    cin>>a>>b>>c>>d>>m;

    for(int i=1;i<=12;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        int p=find(x),q=find(y);
        fa[p]=q;
    }
    for(int i=1;i<=12;i++)cnt[find(i)]++;
    for(int i=1;i<=12;i++)
        if(cnt[i])v.pb(cnt[i]);
    n=v.size();
    dfs(0,0,0,0,0);
    cout<<ans;
    return 0;
}


D.最短路变短了

题意:
有 n 个 点 , m 条 有 向 边 , 每 条 边 带 权 有n个点,m条有向边,每条边带权 nm
问 如 果 翻 转 某 一 条 边 , 权 值 不 变 , 起 点 终 点 互 换 , 是 否 会 让 最 短 路 变 短 问如果翻转某一条边,权值不变,起点终点互换,是否会让最短路变短
询 问 q 次 询问q次 q
题解:
先 用 d i j 算 源 点 为 1 和 源 点 为 n 开 始 的 到 每 个 点 的 最 短 路 径 先用dij算源点为1和源点为n开始的到每个点的最短路径 dij1n
最 初 的 最 短 路 径 也 可 以 算 出 假 设 为 d n 最初的最短路径也可以算出假设为d_n dn
如 果 想 要 某 条 路 翻 转 会 使 得 最 短 路 变 小 如果想要某条路翻转会使得最短路变小 使
说 明 新 的 最 短 路 一 定 会 经 过 这 条 路 说明新的最短路一定会经过这条路
所 以 只 需 要 用 1 到 这 条 边 新 的 起 点 的 距 离    加 上 n 到 这 条 边 新 的 终 点 的 距 离    加 上 这 条 边 的 权 值 所以只需要用1到这条边新的起点的距离~~加上 n到这条边新的终点的距离~~加上这条边的权值 1  n  
看 这 个 值 是 否 小 于 d n , 即 可 判 断 看这个值是否小于d_n,即可判断 dn

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e5+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

struct edge{
    ll u,v,w;
}e[200010];
int n,m;
vector<pll> g1[maxn],g2[maxn];
ll d1[maxn],d2[maxn];
void dij(ll dis[],vector<pll> g[],int s){
    for(int i=1;i<=n;i++)dis[i]=inf*inf;
    dis[s]=0;
    priority_queue<pll,vector<pll>,greater<pll> > q;
    q.push(mp(0,s));
    while(!q.empty()){
        pll p=q.top();
        q.pop();
        int u=p.se;
        if(dis[u]<p.fi)continue;
        for(auto i:g[u]){
            int v=i.fi;
            ll w=i.se;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                q.push(mp(dis[v],v));
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        ll u,v,w;
        cin>>u>>v>>w;
        g1[u].pb(mp(v,w));
        g2[v].pb(mp(u,w));
        e[i]={u,v,w};
    }
    dij(d1,g1,1);dij(d2,g2,n);
    int q;cin>>q;
    while(q--){
        ll x;
        cin>>x;
        if(d1[e[x].v]+d2[e[x].u]+e[x].w<d1[n])cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值