[題解]ABC374 A~E

Sinktank發表於2024-10-06

A - Takahashi san 2

直接判斷字串是否以san結尾即可。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s;
	cin>>s;
	int n=s.size();
	if(s[n-1]=='n'&&s[n-2]=='a'&&s[n-3]=='s') cout<<"Yes";
	else cout<<"No";
	return 0;
}

B - Unvarnished Report

用特殊符號將兩個字串補到相同長度,然後逐位判斷即可。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
string s,t;
int main(){
	cin>>s>>t;
	int n=max(s.size(),t.size());
	s.resize(n,'*'),t.resize(n,'*');
	if(s==t) cout<<"0\n";
	else{
		for(int i=0;i<n;i++){
			if(s[i]!=t[i]){
				cout<<i+1<<"\n";
				break;
			}
		}
	}
	return 0;
}

C - Separated Lunch

\(2^N\)列舉哪些人分配到A組即可,剩下人分到B組。

點選檢視程式碼
#include<bits/stdc++.h>
#define N 30
#define int long long
using namespace std;
int n,a[N],ans=LLONG_MAX;
signed main(){
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=(1<<n)-1;i>=0;i--){
		int cnta=0,cntb=0;
		for(int j=0;j<n;j++){
			if(i&(1<<j)) cnta+=a[j];
			else cntb+=a[j];
		}
		ans=min(ans,max(cnta,cntb));
	}
	cout<<ans;
	return 0;
}

D - Laser Marking

\(n!\)暴力列舉列印順序,然後再\(2^n\)列舉每條線段起始端點。然後就以\(O(2^n\times n!)\)的優秀(大噓)時間複雜度透過了

嘛,反正是暴搜題,能過就行(^^;

此題也有\(O(n^2\times 2^n)\)的做法,見https://atcoder.jp/contests/abc374/editorial/11105,如果有空會回來補這種做法的。

點選檢視程式碼
#include<bits/stdc++.h>
#define N 10
#define int long long
using namespace std;
int n,s,t,sx[N],sy[N],tx[N],ty[N],tmp[N];
double ans=1e18;
double dist(int sx,int sy,int tx,int ty){
	int x=sx-tx,y=sy-ty;
	return sqrt(x*x+y*y);
}
double solve(){
	int x=0,y=0;
	double ans=0;
	for(int ti=0;ti<n;ti++){
		int i=tmp[ti];
		ans+=dist(x,y,sx[i],sy[i])/(1.0*s);
		ans+=dist(sx[i],sy[i],tx[i],ty[i])/(1.0*t);
		x=tx[i],y=ty[i];
	}
	return ans;
}
signed main(){
	cin>>n>>s>>t;
	for(int i=0;i<n;i++){
		cin>>sx[i]>>sy[i]>>tx[i]>>ty[i];
	}
	for(int i=0;i<n;i++) tmp[i]=i;
	do{
		for(int i=(1<<n)-1;i>=0;i--){
			for(int j=0;j<n;j++){
				if((1<<j)&i){
					swap(tx[j],sx[j]);
					swap(ty[j],sy[j]);
				}
			}
			ans=min(ans,solve());
			for(int j=0;j<n;j++){
				if((1<<j)&i){
					swap(tx[j],sx[j]);
					swap(ty[j],sy[j]);
				}
			}
		}
	}while(next_permutation(tmp,tmp+n));
	cout<<fixed<<setprecision(10)<<ans<<"\n";
	return 0;
}

E - Sensor Optimization Dilemma 2

看到需要最大化最小值,於是考慮二分答案\(ans\),判斷使得每個工序的生產能力都達到\(ans\)以上所需要付出的最小錢數是否在\(X\)以內。

難點在於計算當前工序達到\(ans\)的生產能力所需要的最小錢數,如果和揹包一樣,用\(f[i][j]\)表示正在考慮第\(i\)臺機器,達到\(j\)的生產能力所需要的最少錢數,處理每個工序時間複雜度將是\(O(V)\),其中\(V\)\(A\times X=10^9\)同階,表示答案的值域;即使交換鍵值,處理每個工序的時間複雜度也會達到\(O(X)\),總時間複雜度\(O(nX\log V)\),無法透過。

我們可以直接列舉\(S\)機器的個數,有一個結論,假設\(S\)是成本較高的那臺機器,那麼只需要列舉\(S\)的臺數從\(0\)\(\frac{D}{A}-1\),其中\(D=\text{lcm}(A,B)\)。理解起來不難,先把\(ans\)中整的\(D\)全用成本較低的機器達成,對於剩餘的零頭,再列舉用多少臺成本較高的機器。這一步的時間複雜度和\(A\)\(B\)是同階的。

程式碼為了方便直接設\(D=A\times B\),也沒判斷哪個機器成本更低,用\(2\)次迴圈把\(S,T\)都列舉了一遍(只是因為懶而已)。不影響複雜度:\(O(nA\log V)\)

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
#define N 110
using namespace std;
int n,x,a[N],b[N],p[N],q[N];
bool check(int w){
	int ans=0;
	for(int i=1;i<=n;i++){
		int money=LLONG_MAX;//j是S的個數,k是T的個數
		for(int j=0;j<=b[i]&&j*a[i]<=w;j++){
			int k=(w-j*a[i]+b[i]-1)/b[i];
			money=min(money,j*p[i]+k*q[i]);
		}
		for(int k=0;k<=a[i]&&k*b[i]<=w;k++){
			int j=(w-k*b[i]+a[i]-1)/a[i];
			money=min(money,j*p[i]+k*q[i]);
		}
		ans+=money;
		if(ans>x) return 0;
	}
	return 1;
}
signed main(){
	cin>>n>>x;
	for(int i=1;i<=n;i++) cin>>a[i]>>p[i]>>b[i]>>q[i];
	int l=0,r=1e9;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
}