2020年2月6日牛客寒假算法基础集训营2

A-做游戏

题目要求及数据:
在这里插入图片描述
签到题没什么说的
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,c,x,y,z,m,n,p;
int main()
{
	cin>>a>>b>>c;
	cin>>x>>y>>z;
	m=min(a,y);
	n=min(b,z);
	p=min(c,x);
	m+=n+p;
	
	
	cout<<m<<endl;
 } 

B-排数字

题目要求与数据:
在这里插入图片描述

解法:一开始想着找‘6’和‘1‘的顺序然后循环减来着,结果老是错,我忽略了一种情况61616这种的算有两个串,由于可以随意换子串的位置,所以开个数组往上面放记录一下即可

#include<bits/stdc++.h>
#include<string>
using namespace std;
int main()
{
    char s[200010];
    int x=0,y=0,z,sum=0,index=0,a[200010]={0};
     cin>>z;
    scanf("%s",s);
   
    for(int i=0;i<z;i++)
    {
      if(s[i]=='1')
	  x++;   
	   if(s[i]=='6')
	  y++;	
	}
	if(x<1||y<2)
	{
		sum=0;
	}
	else
	{
		a[0]=6;
		y--;
	for(int i=1;i<z;i++)
	{
		if(x>=1&&y>=1) 
		{
			if(a[i-1]=6)
			{
				a[i]=1;
				x--;
			}
				if(a[i-1]=1)
			{
				a[i]=6;
				y--;
				sum++;
			}
		}
		else
		break;
	}
}
	cout<<sum<<endl;
	
	}

C-求概率(还不大会)

题目数据及要求:
在这里插入图片描述

解法:看的一个大佬的解法附上链接:题解链接
在这里插入图片描述
因为任何分数都可以表示成分母为一的形式所以这里让b=1,所以当然a可以为小数,然后用dp做就很简单了,但算出来的结果要再取余不然有的数据太大过不了

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e9+7;
typedef long long ll ;
ll dp[2010][2010],p[2010],n;
int main()
{
   cin>>n;
   for(ll i=1;i<=n;i++)
   {
   	scanf("%lld",&p[i]);
   }
   dp[0][0]=1;//0个题对0道概率肯定为1 
   for(int i=1;i<=n;i++)
       {
       	dp[i][0]=dp[i-1][0]*(maxn+1-p[i])%maxn;//边界 
	   }
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			dp[i][j]=(dp[i-1][j]*(maxn+1-p[i])%maxn+dp[i-1][j-1]*p[i]%maxn)%maxn;//这里最后要再取余maxn一次不然过不了数据,可能数据太大 
		}
	}
	for(int i=0;i<=n;i++)
	  {
	  	printf("%lld ",dp[n][i]);
	  }
}

D-数三角形

题目要求及数据:
在这里插入图片描述

解法:按要求枚举即可,有一点要注意的是,如果三点共线的话有可能求出的边也满足钝角的要求

代码:

#include<bits/stdc++.h>
using namespace std;
struct f{
	int x;
	int y;
}w[510];
int main()
{ int o,sum=0;
 double a, b, c, m,n,q,a1,b1,c1;
 cin>>o;
 for(int i=1;i<=o;i++)
 {
   scanf("%d%d",&w[i].x,&w[i].y);
}

   for(int i=1;i<=o;i++)
   {
   	for(int j=i+1;j<=o;j++)
   	{
   		for(int k=j+1;k<=o;k++)
   		{
   			 a = (w[k].x - w[j].x)*(w[k].x - w[j].x) + (w[k].y - w[j].y)*(w[k].y - w[j].y);
             m = sqrt(a);
              b = (w[k].x - w[i].x)*(w[k].x - w[i].x) + (w[k].y - w[i].y)*(w[k].y - w[i].y);
             n = sqrt(b);
              c = (w[j].x - w[i].x)*(w[j].x - w[i].x) + (w[j].y - w[i].y)*(w[j].y - w[i].y);
             q = sqrt(c);
             a1=(w[k].y-w[j].y)*1.0/(w[k].x-w[j].x);
             b1=(w[k].y-w[i].y)*1.0/(w[k].x-w[i].x);
 if (m + n > q&&n + q > m&&m + q > n)
 {
 	    if(a+b<c||a+c<b||b+c<a)
 	       {    if(a1!=b1)//三点共线也会有可能满足上述情况 
 	       	    sum++; 
			}
		   }
	   }
   }
}
cout<<sum<<endl;
}

E-做计数

题目要求与数据:
在这里插入图片描述

解法:一开始胡乱做的,虽然注意到了ij<=n的要求,但也没想到怎么用,经过这次比赛提醒了我要耐心琢磨题目条件再做题
附上赛后大佬的解法:
在这里插入图片描述
可见只有当i
j凑出的式子为完全平方式才会有一个整数k存在使k成立,又因为i*j<=n所以利用i从1到sqrt(n)来枚举平方数,因为是通过i平方出的式子所以它的因数必有i,通过上面的推导,如果它的因数还有j,那么就会存在一个k从而等式成立,还要注意的是可以i,j换位置如1 4 9,4 1 9算两个不同的式子,当i=j时只有一个式子

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e7+10;
typedef long long ll ;
int main()
{ 
    ll n,i,j,sum=0;
	cin>>n;
	for(i=1;i<=sqrt(n);i++)
	{
		ll k=i*i;
		for(j=1;j<=i;j++)
		{
			if(k%j==0)
			{
				if(i==j)
				sum++;
				else
				sum+=2;
			}
		}
		
		
	 }
	 cout<<sum<<endl; 
}

F-拿物品

题目要求及相关数据:
在这里插入图片描述

解法:这个题一开始理解错了,以为先取的人取a大的值,后取的人取i大的值就行,其实不是的,先取人取一件物品获得了a的同时,后取的人也损失了bi即此时他们的差值多了a+b,所以一贪心,就是取a+b大的。

注意:在结构体里重载运算符之后,本题还得再写一句sort来让它排序,不然不会排序

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll ;
ll n,q[maxn]={0},w[maxn]={0},p=1,o=1;
struct f{
	ll a;
	ll b;
	ll id;
	bool operator <(const f &x)const{
	return a+b>x.a+x.b;
	}
}x[maxn];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x[i].a);
		x[i].id=i;
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x[i].b);
	}
	sort(x+1,x+1+n);
	for(int i=1;i<=n;i=i+2)
	   printf("%lld ",x[i].id);
	   printf("\n");
	   for(int i=2;i<=n;i=i+2)
	   printf("%lld ",x[i].id);
	   printf("\n");
	   
	   
	
	
	
}

G-判正误

题目要求及相关数据:
在这里插入图片描述
重点:附上出题人说的
在这里插入图片描述
可见本题重点是取模

代码1:这样看赛后写的这个代码也是占了pow的空子,等等学点新的方法再写个

#include<bits/stdc++.h>
using namespace std;
const long long maxn=1e9+9;//这个题有点坑,出题人觉得思路容易做就在模数上下了点套 
typedef long long ll ;//取1e9+7/8就不过,取1e9+9就过了 
ll gaoshi(ll a,ll b)//这个函数很好取模数的板子 
{
	if(b==0) return 1;
	if(b%2==1)  return a*gaoshi(a,b-1)%maxn;
	else {
		ll m=gaoshi(a,b/2);
		return m*m%maxn;
		
	}
	
}
int main()
{
	
	int t;
	cin>>t;
	while(t--)
	{
		long long a,b,c,d,e,f,g,x,y,z;
	     scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e,&f,&g);
	   if(g!=0)
	   {
	   x=gaoshi(a,d);
	   y=gaoshi(b,e);
	   z=gaoshi(c,f);
	     
	     if(x+y+z==g)
	     cout<<"Yes"<<endl;
	     else
	     cout<<"No"<<endl;
	 
	 }
	  else{
            if(pow(a,d)+pow(b,e)+pow(c,f)==g) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
	 } 
	
	
}
}

代码2(正解).

#include<bits/stdc++.h>
using namespace std;
const long long maxn=1e9+7;
typedef long long ll ;
ll quick(ll a,ll b)
{   ll ans=1;
	while(b>0){
		if(b&1)
		{
			ans=ans*a%maxn;
		}
		a=a*a%maxn;
		b>>=1;
	}
	return ans;
	
	
 } 
int main()
{
	
	int t;
	cin>>t;
	while(t--)
	{
		long long a,b,c,d,e,f,g,x=maxn,y=0,z=0;
	     scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e,&f,&g);
	     if(quick(a,d)+quick(b,e)+quick(c,f)==g%maxn)
	     cout<<"Yes"<<endl;
	     else
	     cout<<"No"<<endl;
	 } 
	
	
}

H 施魔法

题目要求及相关数据:在这里插入图片描述
在这里插入图片描述
出题人写的前缀和的方法我没搞懂,有空时再琢磨琢磨

解法:我们贪心一下,要让使用的魔力少,则必须在连续的区间中取元素,这个可以通过快排来解决,然后通过这个题我也明白一点动态规划的意义,在这个题中动态规划是一种枚举,是每一个前面可以到达现在这一步的集合,而要取的就是集合中的最小值,因为至少取k个,所以要从k+1开始进行动态规划,对于K+1~n的dp[i](dp[i]代表以i为结尾的所消耗的最小魔力)来说,有两种情况:1.这个dp[i]可以连起来作为前一个dp[i-1]的尾那么状态转移方程就是dp[i]=dp[i-1]-a[i-1]+a[i](注意:一段区间就一个尾和一个头,若成新区间则要变结尾)2.i作为一段(dp[i-k])的新尾,那么一段的头就可以用i-k+1来表示,由于凑成了新的一段,所以头尾都变了,然后加上之前消耗的最小魔力值,方程:dp[i]=dp[i-k]-a[i-k+1]+a[i](保证新一段还是为该段最大值减最小值) 最后取这两种情况的最小值。

代码:

#include<bits/stdc++.h>
using namespace std;
const int w=0x3f3f3f3f;
const int maxn=3e5+10;
typedef long ll;
ll a[maxn],dp[maxn];
int main()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		dp[i]=w;
	}
	sort(a+1,a+1+n);
	dp[k]=a[k]-a[1];
	for(int i=k+1;i<=n;i++)
	{
		dp[i]=min(dp[i-1]-a[i-1]+a[i],dp[i-k]-a[i-k+1]+a[i]);
	}
	
	cout<<dp[n]<<endl;
	
 } 

I.建通道

考察:二进制、权值
还没学待补

J. 求函数

考察:线段树
还没学待补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值