Codeforces Round #637 (Div. 2) - Thanks, Ivan Belonogov!

Codeforces Round #637 (Div. 2) - Thanks, Ivan Belonogov!

A. Nastya and Rice

题目描述

判断 [(a - b) * n,(a + b) *n] 与 [(c - d),(c + d)] 是否有交集。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int SZ = 1e5 + 7;
int n,a,b,c,d;
 
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		scanf("%d%d%d%d%d",&n,&a,&b,&c,&d);
		int l1 = n * (a - b),l2 = n * (a + b);
		int r1 = c - d,r2 = c + d;
		if(l2 < r1 || l1 > r2) printf("No\n");
		else printf("Yes\n");
	}	
	return 0;
} 

B. Nastya and Door

题目描述

给一个长度为n的序列,每个值代表第i个山的高度,当一个山的高度大于前一个山的高度并且大于后一个山的高度,它被认为是山峰。
统计长度k的区间里最大山峰数量,(它被认为山峰的条件是在当前选定区间内有一个比它矮的前一个山峰和一个比它矮的后一个山峰),输出(最大山峰数+ 1)和该区间的左端点,若有多个区间有最大山峰数,输出最小的左端点。

Solution

做前缀和求1 - i的区间的山峰数,然后枚举长度k的区间,注意把边界不合法的山峰去掉。
坑点 : 若最大山峰数为0,题目描述说 左端点 >= 1,所以输出1 1

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int SZ = 2e5 + 7;
int n,k;
int peak[SZ],mount[SZ];
int vis[SZ];
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		scanf("%d%d",&n,&k);
		memset(mount,0,sizeof(mount));
		memset(vis,0,sizeof(vis));
		for(int i = 1;i <= n;i ++) scanf("%d",&peak[i]);
		for(int i = 2;i < n;i ++) 
		if(peak[i] > peak[i - 1] && peak[i] > peak[i + 1])
		mount[i] = 1,vis[i] = 1;
		for(int i = 2;i <= n;i ++)
		mount[i] += mount[i - 1];
		int l = 0,sum = 0;
		if(mount[n] == 0){printf("1 1\n"); continue;} 
		for(int i = 1;i <= n - k + 1;i ++)
		{
			int num = mount[i + k - 1]  - mount[i - 1];
			if(vis[i] == 1) num --;
			if(vis[i + k - 1] == 1) num --;
			if(num > sum)
			{
				sum = num;
				l = i;
			}
		}
		printf("%d %d\n",sum + 1,l);
	}	
	return 0;
} 

C. Nastya and Strange Generator

题目描述

题干非常长,给定了一种生成序列的方法,问所给定的序列是否可以用给定的方法生成出来。

Solution

根据我的理解,从小到大按给定序列开始填数,对于当前填的数,假设是i,那么如果它的下一个位置没填,必须填i + 1,下下个位置若也没填,必须填i + 2,直到一个被填的位置就停止。再选择还没填过的数字去填,然后将按这种方式填出的序列和原序列比对一下,看是否一样。

代码

写的很乱

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int SZ = 1e5 + 7;
int n,num[SZ],pos[SZ];
int vis[SZ];
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		memset(vis,0,sizeof(vis));
		memset(pos,0,sizeof(pos));
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++)
		{
			scanf("%d",&num[i]);
			pos[num[i]] = i;
		}
		bool flag = 1;
		for(int i = 1;i <= n;i ++)
		{
			if(vis[pos[i]] == 0) 
			{
				vis[pos[i]] = i;
				for(int j = pos[i] + 1,t = 1;j <= n;t ++,j ++ )
				{
					if(vis[j] == 0)
					vis[j] = i + t;
					else break;
				}
 			}
 			else if(vis[pos[i]] == 1 && vis[pos[i]] != i) 
 			{
 				flag = 0 ;
 				break;
 			}
		}
		for(int i = 1;i <= n;i ++)
		{
			if(vis[i] != num[i])
			{
				flag = 0;
				break;
			}
		}	
		if(flag == 1) printf("Yes\n");
		else printf("No\n");
	}	
	return 0;
} 

D. Nastya and Scoreboard

题目描述

给定n个数码板,每个上面有7根灯管,你可以操作一次,让灭的灯管变亮。
这些数码板上不同的灯管亮可以显示成数字0 - 9,问能否用k次操作使得这n个数码板都显示出数字,若不行,输出-1,否则求n个数码板上的数排列起来的最大数字。

Solution

自己想的是dp[i][j]为前i个数码板操作j次得到的最大数字,但是2000位的大整数不会操作。
看了题解才懂为什么定义成dp[i][j]为将i - n的数码板操作j次是否可以构成数字。
这样定义状态才能正向贪心的去选取,当第i个选择的时候,
判断 dp[i + 1][当前剩余修改次数 - 这次选择的花费] 是否可行,若可行,则可以选择,这样贪心的去选就可以选出答案。
要明确 899999 永远不如 900000 大,所以只能正着贪心选,定义状态也就得定义i - n的数码板的状态。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 2e5 + 7;
const int INF = 0x3f3f3f3f; 
string Number[10]={"1110111","0010010","1011101","1011011","0111010","1101011","1101111","1010010","1111111","1111011"};
int n,k,val[10],dval[2010];
bool dp[2010][2010];//dp[i][j]为从i - n 选择了点亮 j 根灯管 是否可行 

inline void init()
{
	for(int i = 0;i <= 9;i ++)
		for(int j = 0;j <= 6;j ++)
			if(Number[i][j] == '1')  val[i] |= 1 << (7 - j - 1);
}

inline void solve()
{
	string s;
	for(int i = 1;i <= n;i ++)
	{
		cin >> s;
		for(int j = 0;j <= 6;j ++)
		{
			if(s[j] == '1') dval[i] |= 1 << (7 - j - 1);
		}
	}
	dp[n + 1][0] = 1;
	for(int i = n;i >= 1;i --)
		for(int j = 9;j >= 0;j --)
		{
			if((val[j] & dval[i]) == dval[i])
			{
				int diff = __builtin_popcount(val[j] ^ dval[i]);
				for(int kk = diff;kk <= k;kk ++)
					dp[i][kk] |= dp[i + 1][kk - diff];
			}
		}
	if(!dp[1][k])
	{
		printf("-1\n");
		return;
	}
	string ans = "";
	int remain = k ;
	for(int i = 1;i <= n;i ++)
		for(int j = 9;j >= 0;j --)
		{
			if((val[j] & dval[i]) == dval[i])
			{
				int diff = __builtin_popcount(val[j] ^ dval[i]);
				if(dp[i + 1][remain - diff])
				{
					ans += j + '0';
					remain -= diff;
					break;
				}
			}
		}
	cout << ans << endl; 
}

int main()
{
	init();
	scanf("%d%d",&n,&k);
	solve();
	return 0;
}

2020.4.25

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值