1. 牛客小白月赛65
A. 牛牛去购物
题意:给定n元,购买价格为a元的篮球和价格为b的篮球,数量不定,要使得花掉的钱最多,也就是剩余的钱数最少,求这个值 (1 <= n, a, b <= 1000)
思路:因为数据范围很小, 故直接双重循环暴力枚举购买篮球和足球的数量即可,注意一些取值范围,然后取min可得剩余的钱数最小值
#include
using namespace std;
#define int long long
#define endl '\n'
void solve()
{
int n, a, b;
cin >> n >> a >> b;
int res = 1e8;
for (int i = 0; i <= n / a; i ++)
{
for (int j = 0; i * a + j * b <= n; j ++)
{
res = min(res, n - i * a - j * b);
}
}
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T;
T = 1;
while (T --) solve();
return 0;
}
B.牛牛写情书
题意:给定s1字符串(仅包含小写字母a-z),而后对其某些位置插入s2 = “0123456789±*|,.~!@#$%^&()[]{}'”;:?<>/" 的子串,然后给定s3字符串,询问s1原串中是否有子串等于s3, (字符串长度 <= 5e3)
思路:由于给出的s1是已经被s2插入过的字符串,可以对照着s2字符串,还原出真正的s1字符串,然后普通的字符串查找子串即可, 值得注意的是:对于’\’ 和 ’ " ’ 这两个字符需要在其前面 加一个 ‘\’ 进行转义
#include
using namespace std;
#define int long long
#define endl '\n'
void solve()
{
int n, m;
cin >> n >> m;
string s1, s2;
cin >> s1 >> s2;
string s3 = "0123456789+-*|,.~!@#$%^&()[]{}'\";:?<>\\/";
map<char, int> mp;
for (int i = 0; i < s3.size(); i ++) mp[s3[i]] ++;
string s4;
for (int i = 0; i < s1.size(); i ++)
{
if (mp.count(s1[i])) continue;
s4 += s1[i];
}
int n2 = s2.size();
for (int i = 0; i < s4.size(); i ++)
{
string t = s4.substr(i, n2);
if (t == s2)
{
cout << "YES" << endl;
return;
}
}
cout << "NO" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T;
T = 1;
while (T --) solve();
return 0;
}
C.牛牛排队伍
题意:n个人排队, 1号->2号->3号…, 以此类推n-1号排在n号的前面, 有两个操作,操作1:将x号叫走(保证x号此时一定在队伍中); 操作2:求排在x号前面的是谁
思路:对于这种查询前面是某个人的,很容易想到链表,而且是必须是双向链表,因为我们要做的操作是:将x移走,那么就要求x的前一个人是谁,x的后一个人是谁,所以需要pre数组记录x的前一个人是pre[x],同理suff[x]记录x的后一个是suff[x],然后更新的过程画个图直觉体验更好, ps:(这道题卡vector,vector只能过95%的数据)
#include
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6 + 10;
int pre[N], suff[N];
void solve()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i ++) pre[i] = i - 1;
for (int i = 0; i <= n; i ++) suff[i] = i + 1;
while (k --)
{
int id, x;
cin >> id >> x;
if (id == 1)
{
int t_pre = pre[x];
int t_suff = suff[x];
pre[t_suff] = pre[x];
suff[t_pre] = suff[x];
}
else
{
cout << pre[x] << endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T;
T = 1;
while (T --) solve();
return 0;
}
D.牛牛取石子
题意:有两堆石子,第一堆有a个,第二堆有b个,牛牛先手,牛妹后手,对于取石子有两种方案:(1) 第一堆取1个,第二堆取2个; (2) 第一堆取2个,第二堆取1个;对于选择的方案,必须保证当前石子 >= 取的石子个数才能取,谁先无法取石子,谁就输了,输入两堆石子的个数,判断谁获胜
思路:这个涉及到对称博弈
- 对于后手来说,只要选择与先手对称相反的操作,即先手选择方案1,那么后手就选方案2,这样就可以保证对于两个石堆来说都是 -3,且先手没有办法反制,因此对于两堆石子中的最小值,只要它是 % 3 == 0,那么一定是后手必胜,因为最小值能更快达到0这个必败态
- 那么相反,对于如果 两堆石子中的最小值,它不是 % 3 == 0,比如说是 % 3 == 1, 那么先手就取那个有最小值的石堆石子1个,最大值石堆石子2个,这样就可以保证 有最小石子的石堆 % 3 == 0, 这样又变成了情况1了,因此此时是先手必胜
- 考虑特殊情况,如果这两个石堆的石子数量一样,那么有最小值的余数有3钟情况:0, 1, 2; 分类讨论可得,对于余数为0的情况,后生必胜(1); 对于余数为1的情况,后手必胜(举个例子:(4,4)), 对于余数为2的情况(5, 5), 先手必胜
#include
using namespace std;
#define int long long
#define endl '\n'
void solve()
{
int a, b;
cin >> a >> b;
int r = min(a, b) % 3;
if (r)
{
if (r == 1 and a == b) cout << "niumei" << endl;
else cout << "niuniu" << endl;
}
else cout << "niumei" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T;
cin >> T;
while (T --) solve();
return 0;
}
E. 牛牛的构造
题意:给定数组a的长度n,数组a中的数是1~n的排列,求构造一个数组a,使得这个数组恰好存在k个二元组,满足 1 <= i < j <= n && a[i]-a[j] = 2^x (x属于非负整数) 如果不满足一个数组条件输出-1,反之输出这个数组
思路:考虑到要构成这种二元组,那么一定是逆序的,那么即有构造一个前部分顺序,后部分逆序的数组,这样就可以提供这种二元组,同时要考虑每个数字对于二元组个数的贡献
- 发现对于每个数字i, 满足 i + 2^x <= n,则有 x = log2(n - i), 再 + 1即为每个数的最大贡献,也就是 x + 1,例如对于数字1,它的贡献是 log2(5-1) + 1 = 3,因为向下取整的原因 + 1,同理对于数字2,log2(5-2) + 1 = 2
- 得到每个数字i的贡献后,将k总值减去对应的数字的贡献值,并将其放到最后
- 例如 5 5, 可得 3 4 5 2 1, 因为1贡献了3个二元组,2贡献了2个二元组
#include
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6 + 10;
int flag1[N], flag2[N];
int lg[N];
void solve()
{
int n, k;
cin >> n >> k;
for (int i = 2; i <= n; i ++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= n; i ++) flag1[i] = 1;
for (int i = 1; i < n; i ++)
{
int cha = n - i;
if (lg[cha] + 1 <= k)
{
flag1[i] = 0;
flag2[i] = 1;
k -= lg[cha] + 1;
}
if (k == 0)
break;
}
if (k > 0)
{
cout << -1 << endl;
return;
}
for (int i = 1; i <= n; i ++)
if (flag1[i]) cout << i << " ";
for (int i = n; i >= 1; i --)
if (flag2[i]) cout << i << " ";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T;
T = 1;
while (T --) solve();
return 0;
}