训练round1题解
SMU Spring 2023 Trial Contest Round 1
A.
大意:
给出一个仅由0,1组成的字符串,该字符串是多次在首位各加0或1得到,问最短的原始字符串的长度。
思路:
一次操作增加两个字符,
特判字符串长度为1直接输出1.
首尾双指针进行判断,满足条件同时移动,不满足则退出,答案为原长-2乘以指针移动的次数。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
string s;cin>>s;
if(s.size()==1){
cout<<1<<endl;
continue;
}
int l=0,r=s.size()-1;
while(l<r){
if((s[l]=='0'&&s[r]=='1')||(s[l]=='1'&&s[r]=='0')){
l++;r--;
}else break;
}
cout<<s.size()-l*2<<endl;
}
return 0;
}
B.
大意:
给定一个字符串,把它分成两部分,使每部分重复的字母最少,计算出两部分不重复字母数之和。
思路:
用map记录字母是否重复,
向右扫一遍,向左扫一遍,两个数组记录在每个位置断开后重复的字母数。
最后遍历一遍找出左右重复字母数和最小的那个位置,输出字符串长度-和。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=2e5+10;
vector<int> l(N),r(N);
map<char,int> mp;
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
string s;cin>>s;
for(int i=0;i<s.size()-1;i++){
if(!mp[s[i]]){
mp[s[i]]=1;
if(i==0)l[i]=0;
else l[i]=l[i-1];
}else{
l[i]=l[i-1]+1;
}
}
mp.clear();
for(int i=s.size()-1;i>0;i--){
if(!mp[s[i]]){
mp[s[i]]=1;
if(i==s.size()-1)r[i]=0;
else r[i]=r[i+1];
}else{
r[i]=r[i+1]+1;
}
}
//for(int i=s.size()-1;i>0;i--)cout<<r[i]<<endl;
int mi=1e7;
for(int i=0;i<s.size()-1;i++){
mi=min(l[i]+r[i+1],mi);
}
cout<<s.size()-mi<<endl;
mp.clear();
l.clear();r.clear();
}
return 0;
}
C.
大意:
给出一组数,一次操作可以将相邻两个数符号取反,问经过数次操作后整组数和最大是多少。
思路:
等价于可以任意选两个数取相反符号。
1.若负数个数为偶数,则答案为整组数取正之和。
2.若负数个数为奇数,则看它与最小正数的绝对值,若负数绝对值大则进行操作。
在输入时可以将所有数取正求和,记录负数个数,
若负数个数为奇数,把数排序,删除最小数的2倍。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=2e5+10;
int a[N];
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
ll sum=0;
int cnt=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]<0){
cnt++;
sum-=a[i];
a[i]=-a[i];
}else sum+=a[i];
}
if(cnt&1){
sort(a+1,a+1+n);
cout<<sum-2*a[1]<<endl;
}else{
cout<<sum<<endl;
}
}
return 0;
}
E.
思路:
判断数n是否大等于两个最低需求之和,若是,输出一个最低需求和n-该最低需求。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=2e5+10;
int a[10][10];
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int n;cin>>n;
for(int i=1;i<=4;i++){
for(int j=1;j<=4;j++){
cin>>a[i][j];
}
}
int x,y;
x=min(a[1][1],a[1][2]);
y=min(a[1][3],a[1][4]);
if(x+y<=n){
cout<<1<<' '<<x<<' '<< n-x;
return 0;
}
x=min(a[2][1],a[2][2]);
y=min(a[2][3],a[2][4]);
if(x+y<=n){
cout<<2<<' '<<x<<' '<< n-x;
return 0;
}
x=min(a[3][1],a[3][2]);
y=min(a[3][3],a[3][4]);
if(x+y<=n){
cout<<3<<' '<<x<<' '<< n-x;
return 0;
}
x=min(a[4][1],a[4][2]);
y=min(a[4][3],a[4][4]);
if(x+y<=n){
cout<<4<<' '<<x<<' '<< n-x;
return 0;
}
cout<<-1;
return 0;
}
F.
大意:
给定一组数,(循环式),可选任一个位置开始,给出间隔k,一轮总量是这几个位置上的值相加,问从哪个位置开始使得值最小。
思路:
枚举可以开始的位置:0-k,
数组下标从0开始,这样把位置取个数的模达到循环的作用,
一轮取的个数:n/k
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e5+10;
int a[N];
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int n,k;cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i];
}
ll mi=1e9;
int f=0;
ll sum,tt;
for(int i=0;i<k;i++){
sum=0;
tt=i;
// cout<<"i="<<i<<endl;
// cout<<"n/k-1="<<n/k-1<<endl;
// cout<<"tt="<<tt<<endl;
sum+=a[tt];
// cout<<"sum="<<sum<<endl;
for(int j=1;j<=n/k-1;j++){
tt+=k;
sum+=a[tt%n];
// cout<<"tt%n="<<tt%n<<endl;
// cout<<"sum="<<sum<<endl;
}
if(sum<mi) {
mi = sum;
f = i;
}
}
cout<<f+1;
return 0;
}
G.
大意:
有n种水果,每种水果有对应的taste和cal,要求选m种水果后taste之和除以cal之和等于k。
问taste之和最大是多少。
思路:
把公式变形,得到(taste-k * cal)之和为0。
将(taste-k * cak)看作体积,
taste看作价值,
装满背包,求最大价值。
正负数分开作两个背包,
相同体积的则满足条件(相减为0)。
最后遍历一遍求最大值。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=2e5+10;
const int mod=1e9;
#define inf 0x3f3f3f3f;
int dp[2][100010];
int x,j,ans,n,k,i,a[110],b[110];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
for(i=0;i<2;i++)
for(j=1;j<100010;j++)
dp[i][j]=-1*inf;
dp[0][0]=dp[1][0]=0;
cin>>n>>k;
for(i=0;i<n;i++)cin>>a[i];
for(i=0;i<n;i++){
cin>>b[i];b[i]=a[i]-b[i]*k;
}
for(i=0;i<n;i++){
if(b[i]>0){
for(j=100005;j>=b[i];j--){
dp[0][j]=max(dp[0][j],dp[0][j-b[i]]+a[i]);
}
}else{
x=-1*b[i];
for(j=100005;j>=x;j--){
dp[1][j]=max(dp[1][j],dp[1][j-x]+a[i]);
}
}
}
ans=0;
for(i=0;i<=100005;i++)ans=max(ans,dp[0][i]+dp[1][i]);
if(ans==0)cout<<-1;
else cout<<ans;
return 0;
}
H.
大意:
给出一个n个点m条边的无向图,每条边有区间l-r,求从1到n路径组成的边集,使该边集中所有区间的交内的正整数元素个数最多。
思路:
dfs深搜,要剪枝。
每个结点结构体存区间信息。
剪枝1:已经更新过的区间比现区间大,不合理过了,剪掉。
剪枝2:现区间比已知答案还小,不是最优解,剪掉。
剪枝3:已经访问过的节点剪掉。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e3+10;
const int mod=1e9;
int n,m;
struct node{
int v,le,ri;
};
vector<node> g[N];
int ma;
int vis[N],vx[N],vy[N];
void dfs(int u,int l,int r){
if(u==n){
ma=max(ma,r-l+1);
return ;
}
for(auto t:g[u]){
int vv=t.v,left=t.le,right=t.ri;
if(vis[vv])continue;
vis[vv]=1;
int lll=max(l,left),rrr=min(r,right);
if(vx[vv]<=lll&&vy[vv]>=rrr){
vis[vv]=0;
continue;
}
if(lll<=rrr&&rrr-lll+1>ma) {
vx[vv] = lll;vy[vv] = rrr;
dfs(vv, lll, rrr);
}
vis[vv] = 0;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
int a,b,l,r;
while(m--){
cin>>a>>b>>l>>r;
g[a].push_back({b,l,r});
g[b].push_back({a,l,r});
}
dfs(1,1,1e9);
if(ma==0)cout<<"Nice work, Dima!";
else cout<<ma;
return 0;
}