牛客 周赛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\) 则
所以周长
对边长进行排序后考虑枚举每条边 \(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\) 划分为两个区间和一个区间,所以需要特判