牛客月赛补题报告

目录

木棍游戏:

有趣的区间:

满意的集合:

全体集合:


​​​​​​​

木棍游戏:

题意:给你n个木棍,然后让你组成一个面积最大的三角形。

需要用到的知识点:DFS,and 海伦公式(求三角形的面积)

s = sqrt(l*(l - a)*(l - b)*(l - c));

l是周长的一半,s是三角形的面积。

需要注意的点就是:“可以不用这个木棍。”

可以给A加,也可以给B加,C同样如此。

ac代码:

#include <iostream>
#include <math.h>
using namespace std;
int n;
int A=0,B=0,C=0;
double ans=-1;
int a[20];
void dfs(int u)
{
	if(u>n){
		if(A + B > C && A + C > B && B + C > A);
		else return;
		double p = (A+B+C)/2;
		double s = sqrt(p*(p-A)*(p-B)*(p-C));
		if(s>ans) ans = s;
		return;
	}
	dfs(u+1);
	A+=a[u];
	dfs(u+1);
	A-=a[u];B+=a[u];
	dfs(u+1);
	B-=a[u];C+=a[u];
	dfs(u+1);
	C-=a[u];
}

int main() {
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	dfs(1);
	printf("%.1f",ans);
	return 0;
}

有趣的区间:

way1:直接前缀和➕二分求解:

注意题意:“有一个奇数就可以了,枚举左边界,如果是奇数,那么所有边界任选,如果是偶数,从右边找第一个奇数就可以了。”

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
//int fac[maxn];
int arr[maxn];
int presum[maxn];
int main() {
	int n;
	ll ans=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		ll a;
		scanf("%d",&arr[i]);
		if(arr[i]%2!=0) presum[i]=presum[i-1]+1;
		else presum[i] = presum[i-1];
	}
	for(int i=1;i<=n;i++){
//这里是奇数,所以后面都可以
		if(arr[i]%2) ans+=n-i+1;
		else{
//这里是偶数,所以需要二分找到后面的第一个奇数
			int idx = lower_bound(presum+1, presum+1+n, presum[i]+1) - presum;
			if(idx <= n) ans += n - idx + 1; 
		}
	}
	printf("%lld\n",ans);
	return 0;
}

way2:

先算出区间的所有可能,然后减去偶数区间的:

#include <iostream>
#include <math.h>
using namespace std;
const int maxn = 1e6+10;

int arr[maxn];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int a;
		cin>>a;
		if(a%2!=0){
			arr[i]=1;
		}else{
			arr[i]=0;
		}
	}
	long long lens = (long long)n*(n+1)/2;
	long long len;
	int pre=1;
	for(int i=1;i<=n;i++){
		if(arr[i]%2!=0){
			len = i - pre;
			lens -= (long long)(len+1)*len/2;
			pre = i + 1;
		}
	}
	len = n +1 - pre;//这个地方不是奇数,不能够直接用n
	lens -= (long long)(len+1)*len/2;
	printf("%lld\n",lens);
	return 0;
//	pre = i + 1;
	
}

满意的集合:

这一道题的整体思路就是直接对每种情况进行枚举,dfs。

但是由于数据量过大,所以不作枚举。

要判一个数是否是3得倍数==>只需要把每一位加起来看是不是3的倍数对吧==>也就是说,有x个i,对这个数的数位和的贡献就是x*i==>然后我们把x*i%3等价==>也就是说,只要求出对于i,有多少个x会使得x*i%3=0、1、2就行==>如果i是0,那么x取任何值都使得x*i%3=0(如果i是1,那么x取1、4、7会使得x*i%3=1)

举个例子,

5个1

没有1时,是贡献是0

1个1,贡献是1,

2个1,贡献是2

3个1,贡献是0

4个1,贡献是1

5个1,贡献是2

所以对0的贡献是2,对1的贡献是2,对2的贡献是2

#include <iostream>
typedef long long ll;
using namespace std;
const int mod = 1e9+7;
ll arr[22][22];
ll ans=0;

void dfs(int x,int y,int tmp){
	if(x==10){
		if( y % 3 ==0 ) ans = (ans + tmp) % mod;
		return;
	}
	for(int i=0;i<=2;i++){
		dfs(x+1,y+i,tmp*arr[x][i]%mod);
	}
}

int main() {
	int n;
	int rem;
	for(int i=1;i<=9;i++){
		int a;
		scanf("%d",&a);
		rem = i % 3;
		if(rem==0) arr[i][0]=a+1;
		if(rem==1){
			arr[i][0] = a / 3 + 1 ;
			arr[i][1] = (a+2) / 3 ;
			arr[i][2] = (a+1) / 3 ;
		}
		if(rem==2){
			arr[i][0] = a / 3 + 1 ;
			arr[i][1] = (a+1) / 3 ;
			arr[i][2] = (a+2) / 3 ;
		}
	}
	dfs(1,0,1);
	printf("%lld",ans);
	return 0;
	//首先进行预处理为0,1,2的个数,然后再dfs进行处理
}

全体集合:

#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 1e6+10000;
//首先搞明白题意:就是如果你想要相遇,必须同时在奇数点上或者同时在偶数点上(距离根节点的距离),或者说两者之间的距离为偶数。两种情况,如果说无环图,也就可以看成树,则直接dfs遍历,如果说它没有走过,也就是和树断开了,那么自然就不成立。在环里,如果说已经走过了,那么就是相邻数一样,由此可知这是奇环。



//这一部分就是图的存储and dfs遍历
struct black{
	int value;
	int next;
}arr[maxn];

int len,later[maxn];

//存图
void ins(int x,int y){
	len++;
	arr[len].value = y;
	arr[len].next = later[x];
    later[x] = len;
}

int colum[maxn];
int flag = 0; 
//dfs
void dfs(int x,int col){
	colum[x] = col;
	for(int i = later[x];i;i = arr[i].next){
		//如果你没有走过
		int y = arr[i].value;
		if(!colum[y]) dfs(y,col^1);
		else if(col==colum[y]) flag=1;
	}
}
int b[maxn];
int n,m,k;

int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		ins(a,b);
		ins(b,a);
	}
	for(int i=1;i<=k;i++) scanf("%d",&b[i]);
	dfs(b[1],2);
	//然后这一部分就是叫你怎么判断
	for(int i=1;i<=k;i++){
		if(!colum[b[i]] || (!flag && colum[b[i]]==3 )){
			printf("NO\n");
			return 0;
		}
    }
	printf("YES\n");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值