「Codeforces」 Edu Round #125 (Div. 2)

A. Integer Moves

解题思路

本题只存在三种情况:

  1. 给出点为(0,0)本身,则只需要0
  2. 给出点与(0,0)的距离为整数,则只需要1
  3. 给出点与(0,0)的距离不为整数的情况下,只需要2

对于第3种情况: 第1步移到原点的水平或垂直方向,第2步移到原点

Code
void solve() 
{
    int x, y;   cin >> x >> y;
    int s = sqrt(x * x + y * y);
    
    if (x == 0 && y == 0) puts("0");
    else if (s * s == x * x + y * y) puts("1");
    else puts("2");
}

B. XY Sequence(贪心)

题目大意

给定 n , b , x , y ( x , y > 0 ) n,b,x,y(x,y > 0) n,b,x,y(x,y>0),
对于 a [ i ] a[i] a[i]可以赋值为 a [ i − 1 ] + x 或 者 a [ i − 1 ] − y a[i-1] + x或者a[i - 1] - y a[i1]+xa[i1]y,但要满足 a [ i ] ≤ b a[i] \le b a[i]b ( a [ 0 ] = 0 ) (a[0] = 0) (a[0]=0)
要我们最大化数组 a a a的元素和

解题思路

贪心即可,对于在不超过 b b b的情况下我们可以选择一直加 x x x, 超过的话就减 y y y

Code
void solve()
{
    int n, b, x, y;	cin >> n >> b >> x >> y;
    
    int last = 0, res = 0;  // 不用开数组,直接两个变量记录
    for (int i = 1; i <= n; i++)
    	if (last + x <= b)	last += x, res += last;
    	else last -= y, res += last;
    
    cout << res << endl;
}

C. Bracket Sequence Deletion (模拟\规律)

题目大意

题意:现有一括号串。对于其每个前缀,若以下之一满足:

  • 它是合法括号串
  • 它是长度至少为2的回文串

则删去之。若同时有多个满足,只删去最小的前缀,然后继续以下操作,直到任意前缀都不满足要求。
问:进行了几次删除?最终剩余多少字符?

解题思路

首先我们观察长度为2的括号序列,((,(),))这三种情况都可消除,只有)(需要进行讨论
对于)(这种情况,假设后面出现了一个),则构成)(),因为是回文串,所以可以消除,
如果后面是(则不可删除

OS:感觉就是个找规律题,不过自己的做法并不是最优的,在p佬那里看到一个更优美代码,这里呈现一下

Code
void solve() 
{
    int n, res = 0;	string s;
    cin >> n >> s;
        
    vector<char> v;
    for (auto c : s)
    {
        v.push_back(c);
        if (v.size() == 1)	continue;
        if (*v.begin() == '(' || *v.begin() == v.back())	v.clear(), res++;
    }
    cout << res << ' ' << v.size() << endl;
}

D. For Gamers. By Gamers. (dp/二分)

题目大意

你有 C ( C ≤ 1 0 6 ) C(C \le 10^6) C(C106)个硬币来招募士兵攻打魔王,有 n ( n ≤ 3 ∗ 1 0 5 ) n(n \le 3 * 10^5) n(n3105) 种士兵,招募一个第 i i i 个士兵需要 c o s t [ i ] cost[i] cost[i]个硬币,其攻击力为 d [ i ] d[i] d[i] 血量为 h [ i ] h[i] h[i]。但是招募的时候,只能招募一种士兵(可以招募多个同一个种士兵)。

有多个询问,每个询问给出魔王的攻击力 D D D和血量 H H H,问你能否招募士兵击败魔王,并且使得没有士兵去世。

解题思路

对于魔王,它只要杀死一个士兵即胜利,即需要 h [ i ] D \frac{h[i]}{D} Dh[i]秒,
而对于士兵,士兵是同时攻击的,因此我们可以把攻击力叠加,看为一个整体,
设有 k k k个士兵,则它们击杀魔王需要 H k ∗ d [ i ] \frac{H}{k * d[i]} kd[i]H
为了保证士兵全部存活,我们需要让$\frac{H}{k * d[i]} < $ h [ i ] D \frac{h[i]}{D} Dh[i]
移项 => k ∗ d [ i ] ∗ h [ i ] > H ∗ D k * d[i] * h[i] > H * D kd[i]h[i]>HD, 对于前面这个式子,只有 k k k是变量,其他都为固定值, k k k这时可以理解为倍数,因此进行倍数遍历
我们可以将 d [ i ] ∗ h [ i ] d[i] * h[i] d[i]h[i]看为整体 v a l val val, 并另外构建新数组,记为 m a x v [ i ] = k ∗ d [ i ] ∗ h [ i ] maxv[i] = k * d[i] * h[i] maxv[i]=kd[i]h[i], 含义为“花费 i i i个金币可以得到的最大 v a l val val"
如果士兵的 v a l val val大于魔王的 v a l val val,则可以保证战胜魔王的情况下全部存活

Problem1: 为什么最后是upper_bound而不是lower_bound?
Answer1: 因为lower_bound会找到魔王和士兵同时攻击的状态,这样魔王和士兵都会G

Problem2: 如何理解倍数遍历?
Answer2: 比如第1种类型士兵需要2元,第2种类型士兵需要4元,其他条件理想化,
现我们一共有4元钱,我们可以选择买2只第1种类型士兵或者买1只第2种类型士兵。
也就是通过maxv = max(maxv[2 * cost[1], maxv[cost[1]])这种方式来得到花同样价钱下能得到的最高价值

TIPS: 代码中阐述的test2测试数据点击此处

Code
void solve() 
{
	int n, c;	cin >> n >> c;
	for (int i = 1; i <= n; i++)
	{
		cin >> cost[i] >> d[i] >> h[i];
		maxv[cost[i]] = max(maxv[cost[i]], d[i] * h[i]);  // 预处理值
	}
	
	for (int i = 1; i <= c; i++)  // 倍数遍历
		for (int j = i; j <= c; j += i)
			maxv[j] = max(maxv[j], (j / i) * maxv[i]);
		
	
	// 这一步一定要做,因为可能出现花费比他少,但值比它大的情况
	// 否则会WA在第二个点,测试数据已在上面给出
	for (int i = 1; i <= c; i++)  
		maxv[i] = max(maxv[i - 1], maxv[i]);
		
	int m;	cin >> m;
	while (m--)
	{
		int D, H;	cin >> D >> H;
		if (D * H >= maxv[c])	cout << -1 << ' ';  // 如果花了c元还不能打败魔王,就失败了
		else cout << upper_bound(maxv + 1, maxv + 1 + c, D * H) - maxv << ' ';  // 单调序列,直接二分查找
	}
}

TIPS:如果WA了,那一定是精度问题,本人代码头文件不同

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wiz1code(算法号)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值