Codeforces Round #734 (Div. 3) 题解
题意:
结账时,一共花费n元,要求只使用1元与2元的硬币进行支付,并且两种硬币的数量之差的绝对值要最小,问使用1元与2元硬币的数量各是多少?
思路:
差值最小的话一元钱和二元钱的数量比例要接近1:1,那么总共三元钱, 那么每份都使用的n/3,最后只要判断一下剩下的钱是0, 1, 2元的情况即可。
如果n%3==0,说明两种硬币数量相等,都为n/3。
如果n%3==1,说明还剩1元需要使用面值为1元的硬币进行支付,那么,1元硬币数量为n/3+1,2元硬币数量为n/3。
如果n%3==2,说明还剩2元需要使用面值为2元的硬币进行支付,那么,1元硬币数量为n/3,2元硬币数量为n/3+1。
AC代码
#include<bits/stdc++.h>
typedef long long ll;
int t;
ll n;
using namespace std;
int main()
{
cin>>t;
while(t--)
{
cin>>n;
if(n%3==0) cout<<n/3<<' '<<n/3<<endl;
else if(n%3==1) cout<<n/3+1<<' '<<n/3<<endl;
else cout<<n/3<<' '<<n/3+1<<endl;
}return 0;
}
1151B1. Wonderful Coloring - 1
题意:
n 个字符要么染成红绿俩种颜色,要么不染色。
相同颜色的字符俩俩不同。
红绿俩种颜色的字符数量相同。
满足以上三个条件,每种颜色被染色的字符数量是多少?
思路:
相同的字符每种颜色最多染一个,因此只要对出现次数 ≥2 的字符和出现一次的字符分别讨论就行。
出现次数 ≥2 的字符, 每种颜色都能分到一个,答案加上这些字符的种类数。
出现一次的,红一个,绿一个,答案加上这些字符种类数除以二(向下取整)。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
int main()
{
cin >> t;
while(t--){
string s;
cin>>s;
map<char,int>mp;
for(int i=0;i<s.size();i++) mp[s[i]]++;
int sum1=0,sum2=0;
for(auto i : mp)
{
if(i.second>=2) sum2++;
else sum1++;
}
cout <<sum2+sum1/2<< endl;
}
return 0;
}
1551B2. Wonderful Coloring - 2
题意:
n 个数字,染成 k 种颜色,要么不染色。
相同颜色的数字的值是俩俩不同的。
所有的 k 种颜色的数字数量应该相同。
染色的数字的数量要最多。
用 0 - k 表示染色的颜色种类 (0表示不染色) ,求满足上述条件的染色方案。
思路:
考虑将出现次数 ≥k 的数字和出现次数 <k 的分开讨论。
出现次数 ≥k 的字符, 每种颜色都能分到一个, 所以把位置存储下来直接染色对下标进行标记即可,超过 k 的标为0。
出现次数 <k 的所有数字集中到一起压入容器中, 后对每个数字进行循环染色,从每个相同数字的开始对下标进行标记。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n,k,mp[N];
vector<int>v[N],a;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>t;
while(t--)
{
cin>>n>>k;
a.clear();
for(int i=1;i<=n;i++)
{
mp[i]=0; //初始化每个数字标记为0
v[i].clear();
}
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
v[x].push_back(i);
}
for(int i=1;i<=n;i++)
{
if(v[i].size()>=k) //数量超过k时,对前k个进行标记。
{
for(int j=0;j<k;j++) mp[v[i][j]]=j+1; //对前k个进行标记
}
else //数量小于k
{
for(int j=0;j<v[i].size();j++) a.push_back(v[i][j]); //将其压入a中
}
}
while(a.size()>=k) //进行循环染色
{
for(int j=0;j<k;j++)
{
mp[a.back()]=j+1; //对每个数字的末尾进行标记
a.pop_back(); // 标记完后,进行删除弹出
}
}
for(int i=1;i<=n;i++) cout<<mp[i]<<' ';
cout<<endl;
} return 0;
}
题意:
从 n 个只包含 a、b、c、d、e 的字符串中选择若干个,使得某一个单一字符的出现次数大于其余四个总和,问最多可以选多少个字符串。
思路:
我们可以记录每个字符串 a、b、c、d、e 出现的次数,然后枚举 a、b、c、d、e 分别作为大于其他四个字符总和的情况。
假设当前枚举 a 第 i 个字符串 a 出现的次数为 sum, 其余四个总和为 now ,那么他们的差值 sum - now 为正的时候是可以选的。为了选择更多,我们贪心的将 sum - now 的值从大到小排序,不断加入字符串, 保持总和 ≥0 即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn =2e5+10;
int a[maxn], b[maxn], c[maxn], d[maxn], e[maxn];
int n;
int get_a(){
vector<ll> ans;
for(int i=0;i<n;i++){
ll sum = a[i];
ll now = b[i] + c[i] + d[i] + e[i];
ans.push_back(sum - now);
}
sort(ans.rbegin(), ans.rend());
ll k = 0;
int cnt = 0;
for(int i = 0;i < n;i++){
if(k + ans[i] > 0LL) {
cnt++;
k += ans[i];
}
else break;
}
return cnt;
}
int get_b(){
vector<ll> ans;
for(int i = 0;i < n;i++){
ll sum = b[i];
ll now = a[i] + c[i] + d[i] + e[i];
ans.push_back(sum - now);
}
sort(ans.rbegin(), ans.rend());
ll k = 0;
int cnt = 0;
for(int i = 0;i < n;i++){
if(k + ans[i] > 0LL) {
cnt++;
k += ans[i];
}
else break;
}
return cnt;
}
int get_c(){
vector<ll> ans;
for(int i = 0;i < n;i++){
ll sum = c[i];
ll now = a[i] + b[i] + d[i] + e[i];
ans.push_back(sum - now);
}
sort(ans.rbegin(), ans.rend());
ll k = 0;
int cnt = 0;
for(int i = 0;i < n;i++){
if(k + ans[i] > 0LL) {
cnt++;
k += ans[i];
}
else break;
}
return cnt;
}
int get_d(){
vector<ll> ans;
for(int i = 0;i < n;i++){
ll sum = d[i];
ll now = a[i] + b[i] + c[i] + e[i];
ans.push_back(sum - now);
}
sort(ans.rbegin(), ans.rend());
ll k = 0;
int cnt = 0;
for(int i = 0;i < n;i++){
if(k + ans[i] > 0LL) {
cnt++;
k += ans[i];
}
else break;
}
return cnt;
}
int get_e(){
vector<ll> ans;
for(int i = 0;i < n;i++){
ll sum = e[i];
ll now = a[i] + b[i] + c[i] + d[i];
ans.push_back(sum - now);
}
sort(ans.rbegin(), ans.rend());
ll k = 0;
int cnt = 0;
for(int i = 0;i < n;i++){
if(k + ans[i] > 0LL) {
cnt++;
k += ans[i];
}
else break;
}
return cnt;
}
int main()
{
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 0;i <= n;i++) a[i] = b[i] = c[i] = d[i]= e[i] = 0;
for(int i = 0;i < n;i++){
string s;
cin >> s;
for(int j = 0; j < s.size();j++){
if(s[j] == 'a') a[i]++;
else if(s[j] == 'b') b[i]++;
else if(s[j] == 'c') c[i]++;
else if(s[j] == 'd') d[i]++;
else if(s[j] == 'e') e[i]++;
}
}
int ans = 0;
ans= max(ans, get_a());
ans =max(ans, get_b());
ans = max(ans, get_c());
ans = max(ans, get_d());
ans = max(ans, get_e());
cout << ans << endl;
}return 0;
}
题意:
在 n * m 的网格图放 1×2 (横着)或者 2×1(竖着) 的多米若骨牌,在 n * m 为偶数的情况下,
是否能恰好放 k 个横着的多米洛骨牌。
思路:
考虑为什么题意给的是 n*m 都是偶数,而不是 n、m 都是偶数。
当 n、m 都为偶数的时候, 2×2 的方格可以任意放 2 个 1×2 横着的或者 2×1 竖着的,并且它们都是偶数成对出现的,
因此我们就可以分割图面为若干个 2×2的网格, k 为偶数才能满足条件。
假设若 n 为奇数, 画图不难发现必定要放 m/2 个横着的,同理,若 m 为奇数, 必定要放n / 2 个竖着的,多出来的行列铺满横的或者竖的后就转化偶数情况。
因此只要判断总得减去必定放的是否放得下 k 个并且 k 要大于必定放横的数量。
AC代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t,n,m,k;
cin>>t;
while(t--)
{
cin>>n>>m>>k;
int temp=n*m/2;
if(n%2==1)
{
k-=m/2;
temp-=m/2;
}
if(m%2==1)
{
temp-=n/2;
}
if(k<0||k%2==1||k>temp)
{
cout<<"NO"<<endl; continue;
}
cout<<"YES"<<endl;
}return 0;
}
题意:
在 n ×m 的网格图放 1×2 (横着)或者 2×1(竖着) 的多米若骨牌,在 n×m 为偶数的情况下,是否能恰好放 k 个横着的多米洛骨牌,用字符输出摆放图案。
思路:
按照讨论分别构造即可。
首先先判断有没有奇数的行、列,从 a - z 任意找俩个个字符填充完。
然后将这多的行列暂时删去,当成 n,m 都为偶数填充。
先填充 k 个横着的,剩下的 2×2 填充竖着的即可,唯一的问题就是相邻的字符会重复的问题,我们可以根据下标的奇偶关系来交替填充不同字符,字符从 a−z 自己挑。
如果觉得写的还不错,点个赞吧^ ^