AtCoder Beginner Contest 332 题解

发布时间 2023-12-12 22:53:31作者: CheZiHe929

A - Online Shopping

题目链接

Atcoder

Luogu

简要题意

共有 \(n\) 件商品,第 \(i\) 件商品的价格为 \(p_i\) 日元,数量为 \(q_i\) 件。

除了购买商品所需的的钱数,还要支付运费:如果所买商品的总价小于 \(s\) 日元,那么要支付运费 \(k\) 日元。

问所需要的钱数是多少。

简要思路

模拟即可。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

int n,s,k;
int p,q;
int ans;

signed main(){
	std::cin>>n>>s>>k;
	for(int i=1;i<=n;i++){
		std::cin>>p>>q;
		ans+=p*q;
	}
	if(ans<s)ans+=k;//有运费
	std::cout<<ans<<endl;
	return 0;
}

B - Glass and Mug

题目链接

Atcoder

Luogu

简要题意

有一个容量为 \(g\) 的玻璃杯和一个容量为 \(m\) 的马克杯,你要进行 \(k\) 次以下操作:

  • 如果玻璃杯中装满了水,那么将玻璃杯中的水倒掉;

  • 如果马克杯是空的,就将马克杯中注满水;

  • 否则将马克杯中的水注入到玻璃杯中,直至玻璃杯中水满了或者马克杯中没有水了。

最后数组玻璃杯和马克杯中剩余的水。

简要题意

按照题意模拟。

定义两个数分别用来记录当前玻璃杯和马克杯中剩余的水。

对于第三个操作,我们可以简便操作为对当前玻璃杯还能注入的水的毫升数和马克杯中剩余的水的毫升数取 min,注意提前维护一个值防止其值发生改变。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

int k,g,m;
int glass,mug;//玻璃杯和马克杯当前杯中的水的毫升数

signed main(){
    std::cin>>k>>g>>m;
    for(int i=1;i<=k;i++){
    	if(glass==g)glass=0;//玻璃杯内装满了水
    	else if(mug==0)mug=m;//马克杯是空的
    	else {
    		int now=glass;//提前记录原先玻璃杯中的水
    		glass+=std::min(mug,g-glass);
			mug-=std::min(mug,g-now);//从马克杯往玻璃杯中倒
		}
	}
	std::cout<<glass<<' '<<mug<<endl;
	return 0;
}

C - T-shirts

题目链接

Atcoder

Luogu

简要题意

高桥公司有一个 \(n\) 天的行程 \(s\),在初始时,他们有 \(m\) 件普通衣服。

每天的活动情况为:

如果 \(s_i\)0,那么这一天没有任何安排,可以将之前所有的普通衣服和徽标衣服洗干净;

如果 \(s_i\)1,那么这一天要身穿普通或徽标衣服;

如果 \(s_i\)2,那么这一天要身穿徽标衣服。

注:每件普通衣服和徽标衣服在洗干净之前只能穿一天。

问至少要买多少件衣服才能使每天都有衣服穿。

简要思路

二分答案,寻找最小的购买的衣服数量。

因为徽标衣服是相对“万能”的,所以我们只需二分徽标衣服的数量。

check 判断中,如果能穿普通衣服就先穿普通衣服,否则穿徽标衣服。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

int n,m;
char s[1005];

bool check(int x){
	int num1=m;//普通衣服的数量
	int num2=x;//徽标衣服的数量
	for(int i=1;i<=n;i++){
		if(s[i]=='0'){//无安排
			num1=m;
			num2=x;//将前面所有穿过的衣服洗干净
		}else if(s[i]=='1'){
			if(num1>0)num1--;//能穿普通衣服先穿普通衣服
			else{
				if(num2>0)num2--;
				else return false;
			}
		}else{
			if(num2>0)num2--;//只能穿徽标衣服
			else return false;
		}
	}
	return true;
}

signed main(){
	std::cin>>n>>m>>s+1;
	
	int l=0,r=1000;
	while(l<=r){//二分答案
		int mid=(l+r)>>1;
		if(check(mid))r=mid-1;
		else l=mid+1;
	}
	std::cout<<r+1<<endl;
	return 0;
}

D - Swapping Puzzle

题目链接

Atcoder

Luogu

简要题意

给定两个 \(h \times w\) 的矩阵 \(A,B\),每次操作可以交换矩阵 \(A\) 中相邻两行的所有元素或相邻两列的所有元素。

求至少需要多少次操作才能使 \(A\) 矩阵变化为 \(B\) 矩阵,如果不能实现输出 -1

简要思路

首先注意到:\(2 \leq H, W \leq 5\),因此我们可以大胆猜测这是一个很暴力的做法,不难想到全排列,接下来来详细说明一下做法。

用数组 \(h\) 记录当前矩阵的第 \(i\) 排对应原先 \(A\) 矩阵中的第 \(h_i\) 排。

用数组 \(w\) 记录当前矩阵的第 \(i\) 列对应原先 \(A\) 矩阵中的第 \(w_i\) 列。

全排列枚举每一种交换矩阵的形式,如果符合条件我们就统计当前 \(h,w\) 数组中逆序对的数量,并和当前答案取最小值。

注:对于一个 \(1\)\(n\) 每个数只出现一次的序列,如果想让它变成升序排列,那么至少要进行调换的次数为这个序列的逆序对的数量。

对于不存在答案的情况,我们只需刚开始时对答案赋一个极大的值,最后判断是否等于该值即可。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
const int INF=1e9+5;

int H,W;
int a[10][10],b[10][10];//a,b 矩阵
int h[10],w[10];//枚举全排列所用数组

signed main(){
	std::cin>>H>>W;
	for(int i=1;i<=H;i++)
		for(int j=1;j<=W;j++)std::cin>>a[i][j];
	for(int i=1;i<=H;i++)
		for(int j=1;j<=W;j++)std::cin>>b[i][j];
  	
  	for(int i=1;i<=H;i++)h[i]=i;
  	for(int j=1;j<=W;j++)w[j]=j;//初始赋值,注意从小到大
  	
  	int ans=INF;//赋初始较大值
  	do{
  	  	do{
			bool f=true;
			for(int i=1;i<=H;i++)
				for(int j=1;j<=W;j++)
					if(a[h[i]][w[j]]!=b[i][j]){
						f=false;
						break;
					}
   	  		if(f==false)continue;//判断当前矩阵是否等于 b 矩阵

      		int num=0;
      		for(int i=1;i<=H;i++)
				for(int j=1;j<=H;j++)
					if(i<j&&h[i]>h[j])num++;
			for(int i=1;i<=W;i++)
				for(int j=1;j<=W;j++)
					if(i<j&&w[i]>w[j])num++;//交换次数=两个数组的逆序对数量之和
      		ans=std::min(ans,num);//维护答案

    	}while(std::next_permutation(w+1,w+W+1));
  	}while(std::next_permutation(h+1,h+H+1));//全排列枚举每一种交换情况

  	if(ans==INF)std::cout<<-1<<endl;//答案没有变说明不存在答案
  	else std::cout<<ans<<endl;

  	return 0;
}