8.21 补题

六题

C 16进制世界

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

这是一个16进制的世界,比如522的16进制是20A。

在5月22日那天,有人送给Bob一些月饼,每个月饼有饱食度和幸福度两个属性。

现在Bob有n个月饼,对于每个月饼iii,饱食度为vi,幸福度为wi​。

Bob现在有m饱食度,意味着他吃的月饼的饱食度之和不大于m。

但是由于Bob身处16进制的世界,他吃的月饼的幸福度之和必须是16的倍数。

请帮Bob算一下他最多吃的月饼的数量。

输入描述:

第一行输入两个整数n, m

接下来n行分别输入vi, wi​表示第i个月饼的饱食度和幸福度。

输入数据保证1≤n⋅m≤105。

输出描述:

一个整数,表示Bob最多能吃的月饼数量

示例1

输入

2 5
2 16
3 15

输出

1

思路

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
int dp[100005][16];//dp[k1][k2]表示当前月饼数 k2表示 mod 16的值
signed main() {
    cin >> n >> m;
    vector<int> m1(n);
    vector<int> m2(n);
    for (int i = 0; i < n; ++i) {
        cin >> m1[i] >> m2[i];
    }
    memset(dp, -1, sizeof(dp));
    dp[0][0] = 0;
    for (int i=0;i<n;i++) {
        int v = m1[i];
        int w = m2[i];
        for (int j = m; j >= v; --j) { 
            for (int k = 0; k < 16; ++k) { //遍历0--15 各个位置的dp
                if (dp[j - v][k] != -1) { 
                    int h = (k + w) % 16;
                    //现在跟上一个比较 上一个要加一个
                    dp[j][h] = max(dp[j][h], dp[j - v][k] + 1);
                }
            }
        }
    }
    int res = 0;
    for (int j = 0; j <= m; ++j) {
        res = max(res, dp[j][0]);//找到 k2为0也就是mod 16=0 的数的max月饼数
    }
    cout << res << endl;
    return 0;
}

G 等公交车

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

小G和朋友约好了时间出去玩,他选择坐公交车去找对方。

早早的便来到了公交站牌处开始了等公交车,但公交车却迟迟不来,终于在他濒临爆发的时候,公交车终于缓缓开来。开心的和朋友会合后,他们便开始了一天的玩乐。回到家后,小G还是对于等公交车耿耿于怀,现已知每天都会发出m辆公交车,在手机上它可以查出这m辆公交车的发车时刻第ti 分钟,并且他还知道所有的站点信息,总共有n个公交车站点,第 i 个站点距离发车点的距离为di​ 米。已知公交车的速度为1米/分钟,发车点和所有的站点都在一条直线上, 每次公交车从发车点出发后,依次经过每个站点。他想知道能否设计一个程序,当每次一个乘客在某个时刻时到达某个站点时,可以直接告诉他需要等待的时间?

输入描述:

第一行两个个正整数,表示n,m.
第二行n个正整数,表示n个站点距离发车点的距离di​.(数据保证di​递增)
第三行m个正整数,表示m辆公交车的发车时刻ti​.(数据保证ti递增)
第四行一个正整数Q,表示接下来的询问个数。

接下来Q行,每行两个正整数t,x,表示该乘客在时刻t时来到了x号站点。

对于100%的数据,1≤n,m,q≤105,1≤li,ti≤2×109

输出描述:

Q行,每行一个整数表示需要等待的时间,若该乘客任何公交车都无法乘上,请输出`TNT`

示例1

输入

2 3
2 4
1 3 5
5
3 1
4 1
5 1
8 2
10 2

输出

0
1
0
1
TNT

思路

二分查找

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,d[100005],t[100005],q; 
signed main() {
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	cin>>d[i];
	}
	for(int i=1;i<=m;i++){
		cin>>t[i];
	}
	cin>>q;
	int y=0;
	while(q--){
		int tt,x;cin>>tt>>x;
		int nt=tt-d[x];
		auto y=lower_bound(t+1,t+m+1,nt);
		if(y==m+1+t){
			cout<<"TNT"<<endl;
		}
		else cout<<*y-nt<<endl;
	}
    return 0;
}

H 24点 

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

题目描述

在扑克牌中有种玩法叫做24点,目标是用给定的四张牌通过基本的数学运算(加、减、乘、除)得到24。24点的玩法规则如下:

1. 准备一副扑克牌,去掉大小王,使用 `A,2,3,4,5,6,7,8,9,10,J,Q,K` 分别表示 `1,2,3,4,5,6,7,8,9,10,11,12,13`。每种各四张,共52张牌。
2. 每次从这些牌中任意取出四张牌。
3. 使用这四张牌的数字,通过加法、减法、乘法和除法运算,最终得到24。(除法是正常的数学除法,即有可能出现除不尽的情况,比如 1÷3=1/3
4. 每张牌只能使用一次,可以任意调换数字的顺序,可以使用任意的括号来改变运算顺序。
5. 玩家需要找到至少一种解决方案。如果无法用四张牌得到24点,则说明没有解。

现在需要你判断某种情况下是否有解。

输入描述:

第一行一个正整数 T (1≤T≤1000),表示数据的组数。

接下来 T 行,每行四个字符串,表示取出的四张牌的点数,输入的扑克牌点数只会出现 `A,2,3,4,5,6,7,8,9,10,J,Q,K`。

输出描述:

输出一行一个字符串,如果有解输出 `YES`,无解输出 `NO`

示例1

输入

5
8 2 2 A
5 J 10 4
9 K A A
A 10 10 A
10 8 9 7

输出

YES
YES
YES
NO
YES

思路

暴搜 先处理两个数再处理两个数的加减乘除与另外两个数进行计算 

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t;
vector<double>b; 
string a[5];
int tos(string c){
	if(c=="A")return 1;
	if(c=="J")return 11;
	if(c=="Q")return 12;
	if(c=="K")return 13;
	return stoll(c);
}
bool dfs(vector<double>&b){
	int n=b.size();
	if(n==1&&fabs(b[0] - 24.0) < 1e-6)return 1;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(i!=j){
				vector<double>nb;
				for(int k=0;k<n;k++){
					if(i!=k&&j!=k){
						nb.push_back(b[k]);//剩下的两个数加入新数组 
					}
				}
				for(int op=1;op<=4;op++){
					double res=0;
					//计算原来选定的两个的数 
					if(op==1){
						res=b[i]+b[j];
					}
					if(op==2){
						res=b[i]-b[j];
					}
					if(op==3){
						res=b[i]*b[j];
					}
					if(op==4){
						if(b[j]!=0){
							res=b[i]/b[j];
						}
					}
					nb.push_back(res);
					if(dfs(nb))return 1;
					nb.pop_back();
				}
			}
		}
	}
	return 0;
}
signed main() {
    cin>>t;
    while(t--){
    	b.clear();
    	for(int i=0;i<4;i++){
    	    cin>>a[i];
    	    b.push_back(tos(a[i]));
		}
		if(dfs(b))cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
    return 0;
}

I 正义从不打背身

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

*"正义之枪从不打背身"*。小x最近迷上了无畏契约,钟爱"正义"这把武器。可他使用该武器有一条原则:不击杀(打不中)背对自己的敌人。小x实力强大,对于所有直面自己的敌人都能一击毙命。


在一次游戏中。小x面前有nnn个点位,从左往右序依次为1,2,3,……,n每个点位上都有一个敌人。
每个敌人 要么正面朝向小x,要么背面朝向小x。
小x实力强大,打算强迫这n个敌人玩m轮小游戏之后再对每个人开枪。


游戏规则如下:
在第iii轮小游戏中,依次执行以下所有操作:
* 序号为[1,i]的点位上的敌人位置改变。改变规则为: 从1,2,3,……,i 变为i,i−1,……,3,2,1(原来位于i号位置的敌人更换到1号位置,位于i-1号位置的敌人更换到2号位置……)
* 序号为[1,i]的点位上的敌人原地旋转180°。

所有小游戏过后,小x想知道。目前在每个点位的敌人是否被击败。

输入描述:

第一行输入两个整数 n,m

接下来一行输入一个仅由'P'和‘B'组成的字符串s。si=′P′表示第i个点位的敌人当前正面对小x。si=′B′表示第i个点位的敌人当前正背对小x。

对于所有数据保证:1≤m≤n≤2×106。∣s∣=n

输出描述:

输出一行n个整数。对于第i个数,如果目前位于第i个点位的敌人被击败则输出1,否则输出0。

示例1

输入

3 3
PPP

输出

0 0 1

思路

找规律 6 4 2 1 3 5

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[2000005];
map<int,char>mp;
deque<int> dq;
char ss[2000005], ss1[2000005];
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> ss[i];
    }
    for (int i = 1; i <= m; i++) {
        if ((m - i + 1) % 2 == 1) {
            if (ss[i] == 'P') ss[i] = 'B';
            else ss[i] = 'P';
        }
    }
    int x = m;
    for (int i = 1; i <= (m + 1) / 2; i++) {
        ss1[i] = ss[x];
        x -= 2;
    }
    if (m % 2 == 0) x = 1;
    else x = 2;
    for (int i = (m + 1) / 2 + 1; i <= m; i++) {
        ss1[i] = ss[x];
        x += 2;
    }
    for (int i = 1; i <= m; i++) {
        if (ss1[i] == 'P') cout << 1 << " ";
        else cout << 0 << " ";
    }
    for (int i = m + 1; i <= n; i++) {
        if (ss[i] == 'P') cout << 1 << " ";
        else cout << 0 << " ";
    }
    return 0;
}

K BanG Dream! It's MyGO!!!!! 

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

题目描述

在“BanG Dream! It's MyGO!!!”的世界里,各个乐团的演出和排练场地像星星一样被连接在一起,形成了一张美丽的网络图。每个乐团都有自己独特的演出场地和练习室,这些地点通过各种路径互相连接,组成了一张复杂的图谱。
koala作为一名热爱音乐的乐团忠实粉丝,突然有了一个灵感。他想为最喜欢的乐团设计一个独特的徽章,这个徽章需要从网络图中找出一些特别的图案来代表乐团, 比如选三条边连接到一起, 具体来说,他对以下三种图案感兴趣:
三角形:由三条边构成的连通子图,这是一种经典的图案。
三芒星:四个点形成的图案,一个点连向其他三个点。
闪电折线:一种特别的折线,由四个点按顺序连接, 即一条链。
koala想知道有多少种情况满足,你能帮帮他吗?

输入描述:

第一行包合两个整数
n,m(1≤n≤105,1≤m≤2∗105),依次表示无向图的点数和边数;
接下来 m 行,每行两个整數u,v(1≤u,v≤n1≤u,v≤n1≤u,v≤n),表示一条边(u,v)

题目保证无重边、自环

输出描述:

输出包含一个整数,表示你的答案

示例1

输入

5 5
1 2
1 3
2 3
2 4
3 5

输出

8

说明

满足条件的导出子图的边集分别为:
(1,2),(1,3),(2,3)
(1,2),(2,3),(2,4)
(1,3),(2,3),(3,5)
(1,2),(1,3),(2,4)
(1,2),(1,3),(3,5)
(2,4),(2,3),(3,5)
(1,3),(2,3),(2,4)
(1,2),(2,3),(3,5)

备注:

给定一个无向图,你需要给出三条边的导出子图是连通的情况数量。

思路

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,deg[100005],u[200005],v[200005],cnt1,cnt2,cnt3;
vector<int> ve[100005];
bool vis[100005];
struct edge{
	int v,nex;
}e[200005];
void sjx(){//u,v,w三角形 
	for(int u=1;u<=n;u++){
		for(auto i:ve[u]){
			vis[i]=1;
		}
		for(auto i:ve[u]){
			for(auto j:ve[i]){
				if(vis[j])cnt1++;
			}
		}
		for(auto i:ve[u]){
			vis[i]=0;
		}
	}
}
signed main() {
    cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i];
		deg[u[i]]++;
		deg[v[i]]++;
	}
	for(int i=1;i<=m;i++){
		cnt3+=1ll*(deg[u[i]]-1)*(deg[v[i]]-1);
		if(deg[u[i]]>deg[v[i]] || (deg[u[i]]==deg[v[i]] && u[i]>v[i]))
			swap(u[i],v[i]);
	    ve[u[i]].push_back(v[i]);
	}
	for(int i=1;i<=n;i++)
		cnt2+=1ll*deg[i]*(deg[i]-1)*(deg[i]-2)/6;
	sjx();
	cnt3-=3*cnt1;
    cout<<cnt1+cnt2+cnt3;
    return 0;
}

L koala的程序 

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

题目描述

koala设计了一个程序,首先他写了一个暴力算法,但是在他写完之后受到电脑病毒的影响而失忆。好在他的源代码和题面数据范围保留了下来(数据范围在输入描述中已经给出),但是现在仍然无法通过这个题目。由于电脑病毒的影响,题面已经消失,现在他希望你基于源代码做出优化来解决这道题。

C++代码:

#include <bits/stdc++.h>
using namespace std;

// 链表结构体定义
typedef struct node {
    int id;
    struct node *next;
} *pNode, Node;

// 循环链表头节点
pNode head;

// 按顺序删除的前n-1个节点的id构成的列表
vector<int> ans;

int main() {
    // n个节点,判断值k
    int n, k;
    cin >> n >> k;

    // 构造包含n个节点的循环链表
    head = (pNode)malloc(sizeof(Node));
    pNode now = head;
    for (int i = 1; i <= n; ++i) {
        pNode a = (pNode)malloc(sizeof(Node));
        a->id = i;
        now->next = a;
        now = now->next;
    }
    now->next = head->next;

    // pc为计数器
    int pc = 0;
    now = head;

    // 当前节点的上一个节点
    pNode last = nullptr;

    // 程序执行到链表只剩下最后一个节点为止
    while (n > 1) {
        pc++;
        last = now;
        now = now->next;
        // 当计数器pc = k时从循环链表中删除当前节点,并把被删除的节点的id添加到答案列表ans中
        if (pc == k) {
            last->next = now->next;
            n--;
            pc = 0;
            ans.push_back(now->id);
        }
    }

    // 输出最终答案
    for (auto id : ans) {
        cout << id << ' ';
    }

    return 0;
}

输入描述:

一行包合两个整数 n,k(2≤n≤3∗105,1≤k≤3∗105)

输出描述:

根据题意输出答案

示例1

输入

10 3

输出

3 6 9 2 7 1 8 5 10

思路

使用树状数组来处理节点的插入和删除操作

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 3e5 + 10;
int n, m, maxx;
int bit[maxn];
int lowbit(int x)
{
    return x & -x;
}
void add(int pos, int x)
{
    for (int i = pos; i <= maxx; i += lowbit(i))
        bit[i] += x;
}
int finds(int k)
{
    int ans = 0; 
    int now = 0;  
    for (int i = 30; i >= 0; i--) {
        ans += (1 << i);
        if (ans > maxx || now + bit[ans] >= k) {
            ans -= (1 << i);
        } else {
            now += bit[ans];
        }
    }
    return ans + 1; 
}
signed main()
{
    cin >> n >> m;
    maxx = n;
    for (int i = 1; i <= n; i++)
        bit[i] = lowbit(i);
    int now = 1;
    while (n > 1) {
        now = (now - 1 + m - 1) % n + 1;
        int ans = finds(now);
        add(ans, -1);
        cout << ans << ' ';
        n--;
    }
    return 0;
}

线段树方法

#include <bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn = 3e5 + 5; 
int n, k;
#define lc (p << 1) 
#define rc (p << 1 | 1) 
struct node {
    int l, r, sum, add;
} tr[maxn << 2]; // 大小为4倍的最大范围

// 将更新操作向下传递到子节点
void pushdown(int p) {
    if (tr[p].add) { 
        tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1); // 更新左子节点的和
        tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1); // 更新右子节点的和
        tr[lc].add += tr[p].add; 
        tr[rc].add += tr[p].add; 
        tr[p].add = 0; // 当前节点的更新值清零
    }
}

// 合并左右子节点的结果
void pushup(int p) {
    tr[p].sum = tr[lc].sum + tr[rc].sum; // 当前节点的和等于左右子节点的和之和
}

// 构建线段树
void build(int p, int l, int r) {
    tr[p] = {l, r, r - l + 1, 0}; // 初始化节点的左右边界、和、延迟更新值
    if (l == r) return; // 叶子节点直接返回
    int m = (l + r) >> 1; // 计算中间点
    build(lc, l, m); // 递归构建左子树
    build(rc, m + 1, r); // 递归构建右子树
    pushup(p); // 合并左右子树的结果到当前节点
}

/*void build(int _n) {
    build(1, 1, _n); // 从根节点开始构建线段树
}*/

// 修改线段树,将位置 id 的值设为 0
void mdy(int p, int l, int r, int id) {
    if (l == r) { // 如果是叶子节点
        tr[p].sum = 0; // 将叶子节点的和设为 0
        return;
    }
    pushdown(p); // 否则,将当前节点的更新操作传递给子节点
    int m = (l + r) >> 1; // 计算中间点
    if (id <= m) mdy(lc, l, m, id); // 如果 id 在左子树
    else mdy(rc, m + 1, r, id); // 如果 id 在右子树
    pushup(p); // 更新当前节点的和
}

// 查询区间 [L, R] 的和 现在还有几个人
int count(int p, int l, int r, int L, int R) {
    if (L <= l && r <= R) { // 当前节点完全在查询范围内
        return tr[p].sum; // 返回当前节点的和
    }
    pushdown(p); // 否则,将当前节点的更新操作传递给子节点
    int m = (l + r) >> 1; // 计算中间点
    int ans = 0;
    if (L <= m) ans += count(lc, l, m, L, R); // 如果查询范围在左子树
    if (R > m) ans += count(rc, m + 1, r, L, R); // 如果查询范围在右子树
    return ans; 
}

// 二分查找在区间 [l, r] 中找到第 x 个人的位置
int search(int l, int r, int x) {
    int L = l, mid;
    while (l < r) { // 二分查找过程
        mid = (l + r) >> 1; // 计算中间点
        if (count(1, 1, n, L, mid) >= x) r = mid; // 如果在 [L, mid] 区间中找到第 x 个人
        else l = mid + 1; // 否则在右半部分查找
    }
    return l; // 返回最终位置
}

// 查找第 k 个人的位置
int q(int id, int x) {
    int idx;
    x = (x - 1) % count(1, 1, n, 1, n) + 1; // 第几个 
    //cout<<count(1,1,n,1,n)<<endl;
    if (count(1, 1, n, id, n) >= x) idx = search(id, n, x); // 如果 x 在 [id, n] 区间中
    else {
        x -= count(1, 1, n, id, n); // 计算在左区间中的 x
        idx = search(1, id, x); // 在 [1, id] 区间中查找
    }
    mdy(1, 1, n, idx); // 将找到的位置的值设为 0
    return idx;
}

signed main() {
    cin.tie(0)->sync_with_stdio(false);
    cin >> n >> k;
    build(1, 1, n);
    for (int i = 1, idx = 0; i < n; i++) { // 从 1 开始,找到每个人的位置
        idx = q(idx+1, k); // 查找第 k 个人的位置
        cout << idx << " ";
    }
    return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值