Codeforces Round #783 (Div. 2) A B C

这个codeforce前三题都还行,D题直接gg 感觉和前三题差距太大了,还是太菜了

A题:思维

题目大意:给你一个n行m列的网格,你从位置(1,1)走到 位置 (n,m)的所有路径中,走的步数最少是多少
每一次可以选择四个方向走(上 下 左 右) 但是连续的两次不能走同一个方向 也就是说 假如你上一步走的方向是 向左,那目前这一步走的方向就不能 向左走,其他的方向都都可以

思路:因为(1,1)在左上方,(n,m) 在右下方 所以我们要尽可能 向下 向右走 或者 向右 向下走

如果 n > m的话我们第一步向下走 否则的话 第一步 向右走,这样是最优的

操作 1 如果n行和m列的的差值 <= 1的时候 那我们就可以一直向右然后向下 或者 向下向右交替走 ,这样就不会连续两次走同一个方向,并且最后一步肯定会到达位置(n,m) 这样走的步数是(n + m)- 2

操作 2 如果n行和m列的的差值 > 1的时候
1 你能够向右或者向下走的最大的步数是min(n,m) * 2 - 1 我们可以设定 x = min(n,m) 那从(1,1) 走到(x , x) 那就是操作1 最后一定是走到 (x , x) ,那样走的步数就是 x + x - 2 ,但是走到(x,x)的时候你并没有到(n,m),然后如果是向右走到的(x,x) 这个位置 ,那你下一步的方向一定是向下,(肯定能走,因为n != m)同理 如果是向下走到的(x,x) 这个位置 ,那你下一步的方向一定是向右, 所以再加上这一步就是 x + x - 2 + 1 就是 x + x - 1 就是 min(n,m) * 2 - 1 。
2 接下来就是计算 从(x,x + 1)|| (x + 1,x) 走的(n,m)的步数, 如果你的位置是i,想要走到相邻 的位置并且你不能直接向相邻的方向走 那你的走的步数就是3步 比如 你的位置 是 i 目标位置是 i + 1,走的路径就是先向上,再向右,再向下 ,这样走的步数就是 3 步 ,但是你从i + 1走到i + 2 就需要一步 ,因为你从i 走到i + 1的最后一步 一定不是向右的,所以你在i + 1的位置可以直接向右走 ,也就是1步,你就能从i + 1走到 i + 2。
如果 abs(n - m ) == 2 你就需要 3 步到达 (n,m)
如果 abs(n - m ) == 3 你就需要 3 步 + 1步 = 4 步到达 (n,m)
如果 abs(n - m ) == 4 你就需要 3 步 + 1步 + 3步 = 7 步到达 (n,m)
如果 abs(n - m ) == 5 你就需要 3 步 + 1步 + 3步 + 1= 8 步到达 (n,m)
通过这个为我们可以发现如果abs(n - m)是奇数 那就需要 abs(n - m)* 2 - 2步子 如果abs(n - m)是偶数 那就需要 abs(n - m)* 2 - 1步子 再加上原来的 min(n,m) * 2 - 1 就是答案

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false)
#define cint cin.tie(0)
#define int long long
using namespace std;
 
const int N = 1e5 + 100,mod = 998244353;
 
void solve()
{
	int n,m;cin >> n >> m;
	if(n == 1 && m == 1) cout << "0" << '\n';
	else if(n == 1 && m >= 3 || m == 1 && n >= 3) cout << "-1" << '\n'; // 这个需要特判一下,如果n||m 等于1 并且另外一个n || m 是 >= 3的那一定不行,因为只有1列或者1行只能进行一个方向的移动
	else
	{
		if(abs(n - m) <= 1) cout << n + m - 2 << '\n';
		else
		{
			int u = min(n,m);
			int sum = u + u - 1;
			if(abs(n - m) % 2 == 1) sum += (abs(n - m) * 2 - 2);
			else sum += (abs(n - m) * 2 - 1);
			cout << sum << '\n';
		}
	} 
}
signed main()
{
	ios;cint;
	int t; cin >> t;
	while( t -- ) solve();
	return 0;
}

B: 思维 + 贪心

题目大意:有n个人,m个凳子,围成一个圆圈坐 ,给出每个人的“隐私值“ a[i] ,”隐私值“就是当这个人做凳子的时候,这个人的左.右两边必须留出至少a[i]个空位置,问你能不能找到方法让所有人都坐到凳子上.

思路因为每个人都有"隐私值",所以我们尽量要一个人的隐私值最大化的被另一个人来共用,这样才能尽可能的坐最多的人,因为凳子m是固定的,最差的情况是每个人需要的凳子是 自己做下来的凳子 + 左边隐私值 a[i] + 右边隐私值 = 1 + a[i] + a[i] 这是最差的情况,就是这个人的隐私值没有与另外一个人共同分享,举一个例子 假如 n = 2,a[1] = 4,a[2] = 3 ,第一个人需要的凳子数量是1 + 4 + 4 ,如果第一个人不与第二个人分享他的隐私值那 第二个人需要的凳子数量是 1 +3 +3 他们俩一共需要就是16个凳子,才能做下来,可是如果第一个人与第二个人分享他的隐私值,第一个人需要的凳子数量是 1 + 4 + 4 ,第二个人需要的凳子数量是 1 (自己),一共需要10个凳子就可以了.因为第二个人的隐私值 <= 第一个人的隐私值,所以不需要额外的凳子,
那现在问题就是如何最大化的共享隐私值,我们可以对数组排个升序,从隐私值最大的开始,因为第一个人第一次做座位,所以没有人可以与他共享隐私值,所以第一个需要的凳子的个数一定是1 + a[n] + a[n],第二个人的左隐私值一定会被第一个人所共享,因为第二个人的隐私值一定比第一个人小,所以左边就不需要加上去了,只需要加上右边就行了,重复这个过程,最后一个人左边被倒数第二个人共享,右边被倒数第一个人共享,所以就不用加上隐私值了

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false)
#define cint cin.tie(0)
#define int long long
using namespace std;
 
const int N = 1e5 + 100,mod = 998244353;
int a[N];
void solve()
{
	int n,m;cin >> n >> m;
	for(int i = 1;i <= n;i ++ ) cin >> a[i];
	sort(a + 1,a + 1 + n);
	int sum = 0;int f = 0;
	sum = sum + a[n];
	for(int i = n ;i >= 2;i -- )
	 // 这个最后一个人的位置就不用判断了,如果到倒数第二个人的时候sum还没有到m,
	//那最后一个人一定能做下,因为最后一个人只需要 1个凳子就行
	{
		sum = sum + 1 + a[i];
		if(sum >= m) 
		{
			f = 1;
			break;
		}
	}
	if(f) cout << "NO" << '\n';
	else cout << "YES" << '\n'; 
}
signed main()
{
	ios;cint;
	int t; cin >> t;
	while( t -- ) solve();
	return 0;
}

C:思维

题目大意:有一个数组a,和一个数组b,数组a内的元素全部是正整数,数组b内的元素全部是0,每一次你可以选择两个操作中的一个
操作1 使得 b[i] = b[i] + a[i];
操作2 使得 b[i] = b[i] - a[i];
问你最少多少次可以使得b这个数组变成一个严格的递增序列

思路:

因为要使得次数最少,所以说最好能不使用操作就不使用操作,但是b数组初始为0,a数组有全部是正整数,要使得b数组变成递增序列,最少需要操作n - 1次,有一个b[i]可以不操作,那也就是说b数组中必定有且只有1个0的时候(只有一个位置可以不变,其他位置必须操作使得b变成一个递增序列)才是最优的,(因为不可能出现两个0,出现两个0及以上,b就不是严格的递增序列了,)然后就枚举那个0的位置,看看哪个位置不变是最优的
枚举的那个不变的位置 i 左边全是操作2,右边全是操作1,
b对应的a 操作2 b[i] = b[i] - a[i],b[i]要是递增的,a[i] 又是 > 0,所以要让a[i]是递减的
b对应的a 操作1 b[i] = b[i] + a[i],b[i]要是递增的,a[i] 又是 > 0,所以要让a[i]是递增的

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false)
#define cint cin.tie(0)
#define int long long
using namespace std;
 
const int N = 1e5 + 100,mod = 998244353;
int a[N];
 
signed main()
{
	ios;cint;
	int n; cin >> n;
	for(int i = 1;i <= n;i ++ ) cin >> a[i];
	int ans = 1e18;
	
	for(int i = 1;i <= n;i ++ ) //枚举不变的位置
	{
		int u = a[i];
		int b[N];
		memcpy(b,a,sizeof a);
		a[i] = 0;
		int cnt = 0;int mx = -1;
		for(int j = i;j >= 2;j -- ) // 左边都是操作2,对应的a数组从a[1]到a[i]应该是递减的,
		//那从a[i]到a[1]就是递增的,这个不能从a[1]到a[i]来算递减,因为假如从a[1]到a[i]里面有
		//遇到非递减的情况,加入a[k] <= a[k + 1]那就需要从a[1]到a[k]全部修改,使得a[1]到
		// a[k + 1]每一个元素都满足 前一项比后一项大  
		{
			if(a[j - 1] > a[j])
			{
				cnt++;
				mx = a[j - 1];
			}
			else
			{
				cnt += (mx / a[j - 1] + 1);
				mx = a[j - 1] * (mx / a[j - 1] + 1 );
				a[j - 1] = 	mx;
			}
		}
		
		for(int j = i;j <= n - 1;j ++ ) //左边都是操作1,从a[i]到a[n]应该是递增的
		{
			if(a[j + 1] > a[j])
			{
				cnt++;
				mx = a[j + 1];
			}
			else
			{
				cnt += (mx / a[j + 1] + 1 );	 
				mx = a[j + 1] * (mx / a[j + 1] + 1 );	
				a[j + 1] = mx;
			}
		}
		
		ans = min(cnt,ans);
	
		
		memcpy(a,b,sizeof b);
	} 
	
	cout << ans << '\n';
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值