牛客 周赛86 20250328

牛客 周赛86 20250328

https://ac.nowcoder.com/acm/contest/104637

A:

题目大意:给定 \(x\) 判断需要多少个 \(y\) 才能凑够

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

void solve(){
	int x,y;
	cin>>x>>y;
	cout<<y/x+(y%x!=0)<<endl;
}

int main()
{
	cintie;
	solve();
	
	
	return 0;
}

签到

B:
题目大意:给定 \(n\) 个元素的数组 \(a\) ,可以进行任意次操作,使得区间 \([l,r]\) 中的数被删去,求最后数组最大元素和

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

void solve(){
	int n,k;
	cin>>n>>k;
	LL ans=0;
	for (int i=1;i<=n;i++){
		int a;
		cin>>a;
		if (a>0) ans+=a;
	}
	cout<<ans<<endl;
}

int main()
{
	cintie;
	Trd;
	
	return 0;
}

贪心法,可以删去任意个数,那么保留正数一定是最优的

C:

题目大意:给定由 \(0,1\) 组成的字符串 \(s\) ,可以进行任意次操作,每次可以令两个相邻且相同的元素消去,为了使最后的字符串最小,在操作前可以将 \(k\) 个字符修改为 \(0,1\)\(k\) 的最小值

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

int n;

void solve(){
	cin>>n;
	string s;
	cin>>s;
	stack<char> st;
	st.push(s[0]);
	for (int i=1;i<s.size();i++){
		if (st.size()&&s[i]==st.top())
			st.pop();
		else
			st.push(s[i]);
	}
	cout<<st.size()/2<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

先将字符串依次压入栈,每次入栈的元素需要判断能否和栈顶元素消除,最后栈中的元素一定为 \(0,1,0,1\cdots\) 这样交错排列的

那么我们可以使其中一半的元素转化,令栈内元素可以继续消除,那么 \(k\) 就等于栈内元素个数的一半

D:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

int x,y;
vector<int> v;
int ans=3;

void dfs(int cnt){
	
	if (cnt>3) return;//max=3
	
	for (int i=0;i<v.size();i++){
		for (int j=i+1;j<v.size();j++){
			if ((v[i]&v[j])==0){	
				ans=min(ans,cnt);
				return;
			}
			if ((v[i]^v[j])==0){
				ans=min(ans,cnt);
				return;
			}
		}
	}
	
	v.push_back(x&y);
	dfs(cnt+1);
	v.pop_back();
	
	v.push_back(x|y);
	dfs(cnt+1);
	v.pop_back();
	
	v.push_back(x^y);
	dfs(cnt+1);
	v.pop_back();
	
	v.push_back(__gcd(x,y));
	dfs(cnt+1);
	v.pop_back();
}

void solve(){
	cin>>x>>y;
	v.clear();
	ans=3;
	v.push_back(x);
	v.push_back(y);
	dfs(1);
	cout<<ans<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

简单分析后可以知道最多操作的次数为 \(3\) ,可以通过 \(gcd\) 操作,证明如下:

假设 \(a,b\) 是互质的,那么最多通过两次 \(gcd(a,b)=1\) 得到两个 \(1\) ,然后利用 \(1\oplus1=0\) 凑到

假设 \(a,b\) 是约数关系(假设 \(a|b\)),那么通过 \(gcd(a,b)=a\) 可以凑出两个 \(a\)\(a\oplus a=0\)

所以进行 DFS ,最深递归层数为 \(3\) ,时间复杂度为 \(O(n^4),n=2\) 可以接受

if (cnt>3) return;//max=3
	
for (int i=0;i<v.size();i++){
	for (int j=i+1;j<v.size();j++){
		if ((v[i]&v[j])==0){	
			ans=min(ans,cnt);
			return;
		}
		if ((v[i]^v[j])==0){
			ans=min(ans,cnt);
			return;
		}
	}
}

能得出 \(0\) 的操作只能为 \(a\oplus b,a\bigwedge b\) 所以每层只需要判断两次即可,在 \(k>3\) 时可以剪枝

E:
题目大意:给定 \(n\) 个木棍的长度,判断能组成的封闭凸图形的最小周长

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

int a[110];

void solve(){
	int n;
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	int ans=1e9;
	bool dp[10010]={0};
	dp[0]=1;
	for (int i=1;i<=n;i++){
		for (int j=a[i]+1;j<=10000;j++){
			if (dp[j]){
				ans=min(ans,a[i]+j);
				break;
			}
		}
		for (int j=10000;j>=a[i];j--)
			dp[j]|=dp[j-a[i]];
	}
	if (ans==1e9) cout<<-1<<endl;
	else cout<<ans<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

判断能否组成封闭凸图形,需要满足这样一个性质,设该图形最长边为 \(mx\),其余边为 \(e_i\)

\[\sum_i e_i>mx \]

所以周长

\[c=\sum_i e_i+mx>2*mx \]

对边长进行排序后考虑枚举每条边 \(a_i=mx\),在 \(dp\) 数组内找能组成图形的 \(\sum_i e_i\) 是否存在

这样定义一个 \(dp\) 数组,\(dp_i\) 表示在当前的 \(mx\) 下前 \(i-1\) 条边能否构成 \(\sum_i e_i>mx\)

类似于 \(01\) 背包问题一维空间优化,每轮枚举完 \(a_i\) 后维护背包的状态,由于总边长小于 \(1000\) ,所以容量开到 \(1000\) 即可

F:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 

using namespace std;

LL a[3010],s[3010],b[3010];

void solve(){
	memset(a,0,sizeof a);
	memset(s,0,sizeof s);
	memset(b,0,sizeof b);
	int n;
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i];//前缀和
	for (int i=1;i<=n;i++) b[i]=b[i-1]+a[n-i+1];//后缀和
	for (int k=0;k<=n-3;k++){
		vector<LL> p(n+10),d(n+10);
		for (int i=1;i<=n;i++)
			p[i]=max(p[i-1],s[i]-s[max(0,i-k-1)]);//前i个元素合并k次,k+1个元素的最大值
		for (int i=1;i<=n;i++)
			d[i]=max(d[i-1],b[i]-b[max(0,i-k-1)]);//后i个元素合并k次,k+1个元素的最大值
		LL ans=0;
		for (int i=1;i<=n;i++)
			ans=max(ans,max(p[i-1],d[n-i])-a[i]);
		cout<<ans<<' ';
	}
	cout<<max(s[n]-a[1]-a[1],s[n-1]-a[n])<<' '<<0<<endl;
	
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

有点难懂

假设合并次数 \(k=i\) ,那么合并后的区间内的元素个数为 \(k+1\)

因为所有的 \(a_i>0\) 所以多合并一定不会劣,流程比较清晰,枚举合并的次数,然后枚举每个 \(a_i\)

每次计算区间 \([1,i-1]\) 的元素合并 \(k\) 次和区间 \([i+1,n]\) 的元素合并 \(k\) 次再减去 \(a_i\) 求最大值

边界问题在于怎么考虑 \(i<k+1\) ,即区间的大小比能合并的元素个数少

p[i]=max(p[i-1],s[i]-s[max(0,i-k-1)]);
d[i]=max(d[i-1],b[i]-b[max(0,i-k-1)]);

这里又产生出来了一个隐含的问题,在过程中默认的是将这个数组 \(a\) 划分为了三个区间 \([a_1,a_{i-1}],a_i,[a_{i+1},a_n]\)

然后分别在区间 \([a_1,a_{i-1}]\) 和区间 \([a_{i+1},a_n]\) 中选取连续的 \(k+1\) 个元素和的最大值

\(k=n-2,n-1\) 时,只能将数组 \(a\) 划分为两个区间和一个区间,所以需要特判

posted @ 2025-04-06 22:09  才瓯  阅读(18)  评论(0)    收藏  举报