Codeforces Round #637 (Div. 2) Apr/23/2020 22:45UTC+8

比赛链接 https://codeforces.com/contest/1341
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197

A. Nastya and Rice

在这里插入图片描述

题意:问两个区间有没有交集。
思路:询问有没有交集,直接用没有交集来判断比较好。
[ x 1 , y 1 ] [x1,y1] [x1,y1] [ x 2 , y 3 ] [x2,y3] [x2,y3] y 1 < x 2 y1<x2 y1<x2 或者 x 1 > y 3 x1>y3 x1>y3 的时候没有交集

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
ll t,n,a,b,c,d;

int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>a>>b>>c>>d;
		ll x1=a-b,y1=a+b;
		ll x2=c-d,y2=c+d;
		ll minn=x1*n,maxx=y1*n;
		if(maxx<x2||minn>y2)
			puts("No");
		else
			puts("Yes"); 			
	}
	return 0;
}

B. Nastya and Door

在这里插入图片描述
题意:求峰点数量最多,且最左边的起点。
思路:只需要枚举每个起点,取个峰点最多,起点最靠左的就好了

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

int t,n,k;
int a[maxn],b[maxn],c[maxn];

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]),b[i]=0;
		for(int i=2;i<=n-1;++i)
			if(a[i]>a[i-1]&&a[i]>a[i+1])
				b[i]=1;
		
		for(int i=1;i<=n;++i)
			b[i]+=b[i-1];
		
		int ans=0,index=1;
		for(int i=1;i+k-1<=n;++i)
		{
			int e=i+k-1;
			int sum=b[e-1]-b[i];
			if(ans<sum)
			{
				ans=sum;
				index=i;
			}		
		}
		printf("%d %d\n",ans+1,index);	
	}
	return 0;
}

贴一份比赛时乱七八糟、分类讨论的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

int t,n,k;
int a[maxn],b[maxn],c[maxn];

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]),b[i]=0,c[i]=0;
		for(int i=2;i<=n-1;++i)
		{
			if(a[i]>a[i-1]&&a[i]>a[i+1])
				b[i]=1;
		}
		for(int i=1;i<=n;++i)
			c[i]=c[i-1]+b[i];
		int ans=0,index=-1;
		
		for(int i=2;i<=n-1;++i)
		{
			if(b[i]==1)
			{
				int e=min(n-1,i+k-3);
				int sum=c[e]-c[i-1];
				
				if(ans<sum)
				{
					ans=sum;
					int ss=i-2,tt;
					for(int j=e;j>=i;--j)
					{
						if(b[j]==1)
						{
							tt=i-1+k-1-(j+1);			
							break;
						}
					}
					for(int j=i-1;j>=2;--j)
					{
						if(b[j]==1)
						{
							ss=i-1-(j+1);
							break;
						}
					}		
					if(ss>=tt)
					{
						index=i-1-tt;	
					}
					else
					{
						index=i-1-ss;
					}			
				}	
			}
		}
		index=max(1,index);

		printf("%d %d\n",ans+1,index);	
	}
	return 0;
}

C. Nastya and Strange Generator

在这里插入图片描述
题意:问给定的序列能不能用题中所给的算法生成。
比如,题目中举的例子:原序列a: [ 2 3 * * 1 ],先得出 r 数组 [ 3, 3 ,3 ,4 , * ] 。r 数组的意思是:原序列当前位置右边第一个空格的位置,比如原序列 a 索引为1的位置的右边第一个空位是 3。索引为2的位置的右边第一个空位是 3。索引为4的位置的右边第一个空位是它自己 4 。索引为5的位置的右边没有空位 标记为 *
然后得出count数组,对 r 数组中的出现的数字计数 为 [ 0 , 0 , 3 , 1 ,0 ]。3代表3在 r r r 里面出现了3次。然后count数组中最大的数字出现的位置,就是原序列中选择的位置。这里count数组中索引为3的位置数字最大为3,所以原序列选择第3个位置填充数字。

思路:假设从 1到n 构造一个序列,在放 i 的时候,如果 i - 1 的右边还有空位,就必须放在 i -1 后面。如果 i -1 右边一位没有空位了,就可以任意放。比如,[* * 2 * 1 ],放3的时候,就只能是 [* * 2 3 1] 。然后 4 4 4 就可以随便放,因为3的右边一位已经没有空位了。
所以就遍历一遍看每个数字的位置是否合法。从 1 到 n,检查数字 i 是否合理的时候,这个数字 i 的右边一位,要么是 i +1 ,要么就是被 小于 i 的数字填过了。
实现
记录下每个数字的位置, i 从1 到 n 判定位置是否合法

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

int t,n,a[maxn],pos[maxn],visit[maxn]; 

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&a[i]);
			visit[i]=0;
			pos[a[i]]=i;		
		}	
		visit[n+1]=1;
		bool valid=true;
		for(int i=1;i<=n;++i)
		{
			int p=pos[i];
			visit[p]=1;
			if(i+1==a[p+1]||visit[p+1])
				continue;
			valid=false;
			break;
		}
		if(valid)
			puts("Yes");
		else
			puts("No");	
	}
	return 0;
}

D. Nastya and Scoreboard

在这里插入图片描述
题意:有 n n n 块7位长的电子数字牌(中间缺了一些),给定 k k k 根木棍做补充,问能够组成的最大数字是多少。
在这里插入图片描述

思路

  1. 可以用dfs(i,cnt)表示当前为凑第 i 个数字,已经用了cnt根木棍。每次都贪心的取最大,第一次 i==n+1,cnt==k的时候,就是最终的答案。
  2. dp[i][j]表示从数字牌 i 到 n 用 j 根木棍的方案的集合(只要有一种方案),属性是能否凑成。
    转移: 先判断 第 i 个 牌子 凑成 [0,9]中任意一个数组需要的木棍数cnt。当前需要cnt根,那么 它就是从 j - cnt 转移过来的。
    d p [ i ] [ j ]   ∣ = d p [ i ] [ j − c n t ]   j ∈ [ c n t , k ] dp[i][j] \ |=dp[i][j-cnt] \ j\in[cnt,k] dp[i][j] =dp[i][jcnt] j[cnt,k]
    最后,判断dp[1][k],如果为true就贪心构造。需要 c n t cnt cnt 根并判断 d p [ i + 1 ] [ k − c n t ] dp[i+1][k-cnt] dp[i+1][kcnt] 是否为真

总结

  • 这道题最巧妙的地方是能够看出:完好的数字牌和破损的数字牌之间,可以用二进制之间的关系来作补充
  • 判断 破损数字牌能否变成【1,9】的数字牌: ( v a l [ i ] & t a b [ j ] ) = = v a l [ i ] (val[i]\&tab[j])==val[i] (val[i]&tab[j])==val[i]
  • 判断 能够变成 需要的木棍数:__builtin_popcount((tab[j]^val[i]))
  • 最后构造最大数的时候,是判断取了这一位之后,后一位是否为真。即dp[i+1][k-cnt]是否为真
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;

int n,k;

string numbers[]={"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};

int tab[10];
int val[maxn];
bool dp[maxn][maxn];

int calc(string s)
{
	int res=0;
	for(int i=0;i<7;++i)
		if(s[i]=='1')
			res|=(1<<7-i-1);
	return res;
}

int main()
{
	for(int i=0;i<=9;++i)
		tab[i]=calc(numbers[i]);
	
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
	{
		string s;
		cin>>s;
		val[i]=calc(s); 
	}
	
	dp[n+1][0]=true;
	for(int i=n;i>=1;--i)
	{
		for(int j=0;j<=9;++j)
		{
			if((val[i]&tab[j])==val[i])
			{
				int cnt=__builtin_popcount((val[i]^tab[j]));
				for(int x=cnt;x<=k;++x)
					dp[i][x]=dp[i][x]|dp[i+1][x-cnt];
			}
		}
	}
	
	if(!dp[1][k])
		puts("-1");
	else
	{
		string ans="";
		for(int i=1;i<=n;++i)
		{
			for(int j=9;j>=0;--j)
			{
				if((val[i]&tab[j])==val[i])
				{
					int cnt=__builtin_popcount(val[i]^tab[j]);
					if(dp[i+1][k-cnt])
					{
						k-=cnt;
						ans+=j+'0';
						break;
					}	
				}	
			}	
		}
		cout<<ans<<"\n";	
	}
	return 0;
}

DFS

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

int n,k;
int val[10],now[maxn];
bool visit[maxn][maxn];
int ans[maxn];
string numbers[]={"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};

int f(string s)
{
	int res=0;
	int n=s.size();
	for(int i=0;i<n;++i)
	{
		if(s[i]=='1')
			res|=(1<<n-1-i);
	}
	return res;
}

void dfs(int i,int cnt)
{
	if(cnt>k||visit[i][cnt])
		return;
	visit[i][cnt]=true;
	
	if(i==n+1)
	{
		if(k==cnt)
		{
			for(int i=1;i<=n;++i)
				printf("%d",ans[i]);
			exit(0);
		}
		return;
	} 
	
	for(int j=9;j>=0;--j)
	{
		ans[i]=j;
		if((val[j]&now[i])==now[i])
			dfs(i+1,cnt+__builtin_popcount(val[j]^now[i]));
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=0;i<=9;++i)
		val[i]=f(numbers[i]);
	
	for(int i=1;i<=n;++i)
	{
		string s;
		cin>>s;
		now[i]=f(s);
	}
	dfs(1,0);
	puts("-1");
	return 0;
}

E. Nastya and Unexpected Guest(01BFS)

在这里插入图片描述

题意:在一条长度为 n的线段上,一个人要从0 到达n。线段[0,n]上有很多的安全岛。一个人只能在绿灯的时候才可以移动而且绿灯时必须在移动,红灯时必须待在岛上。问这个人从0到n最短需要的时间。
思路:用dis[i][j]表示当前在第i个岛,绿灯时间为 j j j 经历过的红绿灯轮次。0/1BFS的时候,记录两个量 pos和sec,pos表示当前是第几个岛,sec表示经过的绿灯秒数。那么在移动时,当sec<g时,权值为0,sec==g时权值为1,sec>g时,说明无法到达当前岛屿直接舍弃

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+5,mod=1e9+7;
const int inf=0x3f3f3f3f;

int n,m,g,r;
int d[maxn];
bool visit[10010][1010];
int times[10010][1010];

struct node
{
	int pos,sec;
};

int bfs()
{
	deque<node> q;
	q.push_back({1,0});
	visit[1][0]=true;
	int ans=inf;
	while(!q.empty())
	{
		node t=q.front();
		q.pop_front();
		if(t.sec==0)
		{
			if(g>=d[m]-d[t.pos])
			{
				ans=min(ans,d[m]-d[t.pos]+times[t.pos][t.sec]*(g+r));
			}
		}
		
		if(t.pos>1)
		{
			int pos=t.pos-1;
			int tt=d[t.pos]-d[pos]+t.sec;
			if(tt<g&&!visit[pos][tt])
			{
				visit[pos][tt]=true;
				q.push_front({pos,tt});
				times[pos][tt]=times[t.pos][t.sec];
			}
			else if(tt==g&&!visit[pos][0])
			{
				visit[pos][0]=true;
				q.push_back({pos,0});
				times[pos][0]=times[t.pos][t.sec]+1;
			}
		}
		if(t.pos<m)
		{
			int pos=t.pos+1;
			int tt=d[pos]-d[t.pos]+t.sec;
			if(tt<g&&!visit[pos][tt])
			{
				visit[pos][tt]=true;
				q.push_front({pos,tt});
				times[pos][tt]=times[t.pos][t.sec];
			}
			else if(tt==g&&!visit[pos][0])
			{
				visit[pos][0]=true;
				q.push_back({pos,0});
				times[pos][0]=times[t.pos][t.sec]+1;
			}			
		}		
	}
	return ans;	
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
		scanf("%d",&d[i]);
	sort(d+1,d+1+m);
	scanf("%d%d",&g,&r); 
	int ans=bfs();
	if(ans==inf)
		ans=-1;
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值