AtCoder Beginner Contest 374 A-E 题解

服了,跟 DP \text{DP} DP 杠上了,C 和 E 都在想 DP \text{DP} DP
C 和 D 又交了两发罚时

pAGbU00.png

每题难度:
A:11
B:28
C:226
D:694
E:1504
F:2026
G:2608

A. Takahashi san 2

题意

给你一个字符串,判断这个字符串是否以 san 结尾,是输出 Yes,否则输出 No

思路

使用 string 中的 .substr() 函数,若 s.substr(s.length()-3,3)=="san" 输出 Yes,否则输出 No

注:s.substr(i,len),表示从字符串 s s s 的第 i i i 为开始的 l e n len len 个字符组成的字符串(子串)

C++ 代码
#include<bits/stdc++.h>
using namespace std;
string s;
int main(){
	cin>>s;
	if(s.substr(s.length()-3)=="san"){
		cout<<"Yes"<<endl;
	}else{
		cout<<"No"<<endl;
	}
	return 0;
}

B. Unvarnished Report

题意

输出两个字符串 S S S T T T 第一个不同的位置

思路

将两个字符串用特殊符号补全,使它们长度相等即可判断

C++ 代码
#include<bits/stdc++.h>
#define sz(v) (int)v.size()
using namespace std;
string s,t;
int main(){
	cin>>s>>t;
	while(sz(s)<sz(t)){
		s+="?";
	}
	while(sz(t)<sz(s)){
		t+="!";
	}
	s=" "+s,t=" "+t;
	for(int i=1;i<=sz(s);i++){
		if(s[i]!=t[i]){
			cout<<i<<endl;
			return 0;
		}
	}
    cout<<0<<endl;
	return 0;
}

C. Separated Lunch

题意

n n n 个数,将它们分成两组,设这两组的和分别为 a a a b b b,求 max ⁡ ( a , b ) \max(a,b) max(a,b) 的最小值

思路

一开始以为是动态规划,结果直接 CE \color{#DFDF00}{\text{CE}} CE

暴力枚举每一个选分到第一组还是第二组,用二进制数表示,最多 2 20 2^{20} 220 次循环,时间足够

错误 C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=105;
const int maxm=1e8+5
int v[maxn];
int sum;
bool dp[maxn][maxm];
signed main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>v[i];
		sum+=v[i];
	}
	dp[0][0]=1;
	for(int i=0;i<n;i++){
		for(int j=0;j<=sum;j++){
			dp[i+1][j+v[i]]=dp[i+1][j+v[i]]||dp[i][j];
			dp[i+1][j]=dp[i+1][j]||dp[i][j];
		}
	}
	int ans=sum;
	for(int i=0;i<=sum;i++){
		if(dp[n][i]){
			ans=min(ans,max(i,sum-i));
		}
	}
	cout<<ans<<endl;
	return 0;
}
正确 C++ 代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=25;
int n;
int k[maxn];
signed main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>k[i];
	}
	int mn=inf;
	for(int i=1;i<(1<<n);i++){
		int mask=i;
		int cnt=0;
		int a=0,b=0;
		while(cnt<n){
			if(mask&1) a+=k[cnt];
			else b+=k[cnt];
			mask>>=1;
			cnt++;
		}
		mn=min(mn,max(a,b));
	}
	cout<<mn<<endl;
	return 0;
}

D. Laser Marking

题意

一个机器,要画出平面上的 n n n 条线段,每条线段从 ( A i , B i ) (A_i,B_i) (Ai,Bi) ( C i , D i ) (C_i,D_i) (Ci,Di)

画线时每秒移动 T T T 个单位,不画线时每秒移动 S S S 个单位

一开始机器在位置 ( 0 , 0 ) (0,0) (0,0),你可以从 ( A i , B i ) (A_i,B_i) (Ai,Bi) ( C i , D i ) (C_i,D_i) (Ci,Di) 画一条线段,也可以从 ( C i , D i ) (C_i,D_i) (Ci,Di) ( A i , B i ) (A_i,B_i) (Ai,Bi) 画一条线段,一条线段不能中断

忽略抬笔和落笔的时间,问至少需要多少秒才能画完所有线段,误差需要在 1 0 − 6 10^{-6} 106 以内

思路

因为 N ≤ 6 N \le 6 N6,所以使用 next_permutation() 函数来枚举画线顺序,

在每个排列中,要使用二进制数枚举画线起点和终点

结果取最小值即可

注:两点间距离公式为 dist ( ( A i , B i ) , ( C i , D i ) ) = ( C i − A i ) 2 + ( D i − B i ) 2 \text{dist}((A_i,B_i),(C_i,D_i)) = \sqrt{(C_i-A_i)^2+(D_i-B_i)^2} dist((Ai,Bi),(Ci,Di))=(CiAi)2+(DiBi)2

C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long double ld;
int n;
int s,t;
int a[10],b[10],c[10],d[10];
int pmt[10];
ld res=3e18;
int sqr(int a){
	return a*a;
}
signed main(){
	cin>>n>>s>>t;
	for(int i=1;i<=n;i++){
		pmt[i]=i;
		cin>>a[i]>>b[i]>>c[i]>>d[i];
	}
	do{
		for(int i=0;i<(1<<n);i++){
			ld ans=0;
			int x=0,y=0;
			for(int j=1;j<=n;j++){
				int cur=pmt[j];
				if(i&(1<<(j-1))){
					ans+=sqrt(sqr(c[cur]-x)+sqr(d[cur]-y))/s;
					x=a[cur],y=b[cur];
				}else{
					ans+=sqrt(sqr(a[cur]-x)+sqr(b[cur]-y))/s;
					x=c[cur],y=d[cur];
				}
				ans+=sqrt(sqr(a[cur]-c[cur])+sqr(b[cur]-d[cur]))/t;
			}
			res=min(ans,res);
		}
	}while(next_permutation(pmt+1,pmt+1+n));
    
	cout<<fixed<<setprecision(20)<<res<<endl;
    
	return 0;
}

E. Sensor Optimization Dilemma 2

AT又在E放二分答案!!!

题意

制造某个产品有 N N N 个步骤,第 i i i 个步骤有两个机器可以完成:

  • 机器 1 1 1:每台 P i P_i Pi 元,每天可以生产 A i A_i Ai 个产品
  • 机器 2 2 2:每台 Q i Q_i Qi 元,每天可以生产 B i B_i Bi 个产品

你共有 x x x 元钱来购买机器,不一定要全用完。

求出这个值的 最大值 min ⁡ i = 1 N { 第  i  天生产的产品数 } \min_{i=1}^N \{第\ i\ 天生产的产品数\} mini=1N{ i 天生产的产品数}

思路

二分答案:

二分查找这个 min ⁡ \min min 值,所以每个步骤就至少得每天生产这么多个产品,求出总钱数判断是否 ≤ x \le x x

C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=105;
int n,x;
int a[maxn],b[maxn],p[maxn],q[maxn];
bool check(int cur){
	int sum=0;
	for(int i=0;i<n;i++){
		int cost=3e18;
		for(int j=0;j<100;j++){
			int rest=max(cur-j*b[i],0ll); //若用j台机器2,还需生产多少个产品
			int k=(rest+a[i]-1)/a[i]; //还需要多少台机器1
			cost=min(cost,j*q[i]+k*p[i]); //取第i个步骤的金额最小值
		}
		sum+=cost; //计算总价钱
		if(sum>x){
			return false;
		}
	}
	return true;
}
signed main(){
	cin>>n>>x;
	for(int i=0;i<n;i++){
		cin>>a[i]>>p[i]>>b[i]>>q[i];
		if(p[i]*b[i]>q[i]*a[i]){
			swap(a[i],b[i]);
			swap(p[i],q[i]);
		}
	}
	int l=0,r=2e9;
	while(l<r){
		int mid=(l+r)/2;
		if(check(mid)){
			l=mid+1;
		}else{
			r=mid;
		}
	}
	cout<<l-1<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值