Codeforces Round #734 (Div. 3)個人題解
博主前言:萌新第一次寫題解,還請dalao們輕點噴QAQ
比賽鏈接:Codeforces Round #734 (Div. 3)
A題 Polycarp and Coins
題目大意:
你有兩種硬幣,一種是一元的,一種是兩元的,給你一個數,讓你用這兩種硬幣來表示,並使這兩種硬幣數量差值最小,輸出這個最小數量。
思路解析:
首先兩種硬幣面值之和為3,所以如果給出的面值 n 為3的整數倍,我們就可以用相同的兩種硬幣數量來表示,即 ans = n / 3 ,否則 n 對 3 取模,如果為 1 ,我們就可以增加一個1元面值的硬幣,如果為 2 ,就可以增加一個2元面值的硬幣。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int main(){
int t;
cin>>t;
while(t--){
ll n;
cin>>n;
int ans=n/3;
if(n%3==0)
cout<<ans<<" "<<ans<<endl;
else if(n%3==1)
cout<<ans+1<<" "<<ans<<endl;
else
cout<<ans<<" "<<ans+1<<endl;
}
return 0;
}
B題 Wonderful Coloring - 1(easy)
題目大意:
我們需要給一個字符序列染色,對於每個字符元素,我們有三種選擇,要么塗紅色,要么塗綠色,要不什么也不塗,並且要求滿足字符序列中相同字母不會被塗成同一顏色,而且塗紅色的次數和綠色相等,求紅色最多塗了多少。
思路解析:
我們首先可以對序列中的字符分成兩類,一類是數量大於2的字符,一種是數量為1的字符,對於數量大於二的字符,我們可以讓其中的兩個分別塗成紅色和綠色,多出來的只能選擇不塗,對於數量為1的字符,我們可以把他們歸為一類,並且給他們兩兩分組,分別塗紅色綠色,如果分不完,有單個剩下的,那么剩下的這一個只能不塗,才能保證最大化紅色的數量。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
ll a[maxn],sum[maxn];
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
memset(sum,0,sizeof sum);
int a=0,b=0;
for(int i=0;i<s.size();i++){
sum[s[i]-'a']++;
}
for(int i=0;i<=26;i++){
if(sum[i]==1)a++;
if(sum[i]>=2)b++;
}
cout<<b+a/2<<endl;
}
return 0;
}
B2題 Wonderful Coloring - 2(hard)
題目大意:
這是B1更復雜的版本,相比B1,他將紅綠兩種顏色拓展為k種顏色,更加不同的是,在這一個版本中,你需要輸出你的塗色方案。
思路解析:
我們可以類比B1的做法,同樣講元素分成兩類,一類是數目大於等於k的,一種是數目小於k的,對於數目大於等於k的,我們可以將其中的k個分別塗k種顏色,而其他多出來的,只能不塗,對於數目小於k的,我們將它們放在一起塗色,而這里為了保證相同數字塗不同顏色,我們可以對這些元素先排序,再分別給她們塗色,這樣就能保證所給的條件。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int a[maxn];
int sum[maxn],p[maxn],vis[maxn],ans[maxn];
struct node{
int val,rank,vis,ans;
}b[maxn];
bool cmp(node x,node y){
return x.val<y.val;
}
bool cmpp(node x,node y){
return x.rank<y.rank;
}
int main(){
int t;
cin>>t;
while(t--){
memset(sum,0,sizeof sum);
memset(vis,0,sizeof vis);
memset(p,0,sizeof p);
int n,k,maxx=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[a[i]]++;
}
for(int i=1;i<=n;i++){
if(sum[a[i]]>=k)vis[i]=1;
}
int now=0,tot=0;
for(int i=1;i<=n;i++){
if(vis[i]){
if(p[a[i]]<k){
p[a[i]]++;
ans[i]=p[a[i]];
}
else ans[i]=0;
}
else {
vis[i]=10;
tot++;
now++;
if(now%k==0)ans[i]=k;
else
ans[i]=now%k;
}
}
for(int i=1;i<=n;i++){
b[i].val=a[i];
b[i].ans=ans[i];
b[i].vis=vis[i];
b[i].rank=i;
}
sort(b+1,b+n+1,cmp);
tot=tot/k*k;
int cnt=0;
for(int i=1;i<=n;i++){
if(b[i].vis==10){
cnt++;
if(cnt<=tot){
if(cnt%k==0)b[i].ans=k;
else
b[i].ans=cnt%k;
}
else b[i].ans=0;
}
}
sort(b+1,b+n+1,cmpp);
for(int i=1;i<=n;i++)cout<<b[i].ans<<" ";
cout<<endl;
}
return 0;
}
C題 Interesting Story
題目大意:
一個作家有n個單詞,每個單詞只由 a,b,c,d,e 組成,並寫一個故事。而一個有趣的故事需要滿足:故事中存在一個字母的個數比其他字母個數的總和還更多,比如:bac,aaada,e 三個單詞,a 出現的次數比其他 4 種單詞出現次數都多,我們需要求最多的單詞數量來使故事有趣。
思路解析:
我們可以直接枚舉每種字符作為文章中出現最多的字符,然后對於每個單詞來說我們可以把其他字符大於出現最多字符的單詞叫做可刪單詞,然后貪心的去掉可刪字符直到滿足文章有趣的條件,如果都無法滿足有趣條件,就輸出0。(代碼參考dalao代碼QAQ我太菜了QAQ)
AC代碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int L = 5;
vector<int> balance[L];
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
for (int i = 0; i < L; i++)
balance[i].clear();
for (int i = 1; i <= n; i++)
{
string s;
cin >> s;
int initBalance = -(int)s.length();
for (int j = 0; j < L; j++)
balance[j].push_back(initBalance);
for (auto c : s)
balance[c - 'a'].back() += 2;
}
int bestCount = 0;
int bestLetter = 0;
for (int i = 0; i < L; i++)
{
auto& b = balance[i];
sort(b.begin(), b.end());
reverse(b.begin(), b.end());
if (b[0] <= 0) continue;
int sumBalance = b[0];
int j = 1;
for (; j < n && sumBalance > 0; j++)
sumBalance += b[j];
if (sumBalance <= 0) j--;
if (j > bestCount)
{
bestCount = j;
bestLetter = i;
}
}
cout << bestCount << endl;
}
return 0;
}
D1題 Domino (easy version)
題目大意:
我們有一個 n * m ( n * m 為偶數即 n 和 m 至少有一個偶數)的格子圖,我們需要用多米諾骨牌來鋪滿這個圖,每個骨牌占據兩個格子的空間,我們放置骨牌有兩種方式,一種是豎着放,一種是橫着放,骨牌不可以重疊,給你橫着的骨牌的數量,問你是否有方案可行,只需輸出 YES 或者 NO。
思路解析:
我們可以分成三種情況考慮:1. n ,m 都為偶數 2. n為偶數,m 為奇數 3. n 為奇數,m 為偶數。然后我們考錄,如果格子是一個 22 的格子圖,我們可以用兩個豎着的骨牌或者橫着的骨牌來鋪滿它。基於這種防暑,我們來分別考慮三種情況:1. 可以直接轉化成多個 22 正方塊的疊加,我們就可以求出最多的橫骨牌數量,與給定數量比較即可。 2. 因為 m 為奇數,我們把他看成情況一外加一列格子來表示,而多出的一列只能用豎着的骨牌來填滿 3. 因為 n 為奇數,我們把他看成情況一外加一行格子來表示,而多出的一行只能用橫着的骨牌來填滿(三種情況示意圖如下)計算最大橫着的骨牌數量分別與給定數量比較即可。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,m,k;
cin>>n>>m>>k;
if(n%2==0){
int sum=(n/2)*(m/2);
if(k>sum*2){
cout<<"NO"<<endl;
continue;
}
else {
if(k%2==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
else {
int sum=(n/2)*(m/2)+m/2;
if(k>sum*2||k<m/2){
cout<<"NO"<<endl;
continue;
}
else {
if((k-m/2)%2!=0)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}
}
return 0;
}
D2題 Domino (hard version)
題目大意:
在D1的基礎上,我們需要輸出骨牌鋪滿格子的方案。
思路解析:
在D1中,我們已經有了如何構造的方式,按D1思路構造即可。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
char a[maxn][maxn];
int ck(int n,int m,int k){
if(n%2==0){
int sum=(n/2)*(m/2);
if(k>sum*2)return 0;
else {
if(k%2==0)return 1;
else return 0;
}
}
else {
int sum=(n/2)*(m/2)+m/2;
if(k>sum*2||k<m/2)return 0;
else {
if((k-m/2)%2!=0)return 0;
else return 1;
}
}
}
void init(){
for(int i=1;i<=105;i++)
for(int j=1;j<=105;j++)
a[i][j]='0';
}
void pt(int n,int m){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<a[i][j];
}
cout<<endl;
}
}
void bt(int n,int m,int k){
init();
if(n%2==1){
for(int i=1;i<=m/2;i++){
a[n][i*2-1]=a[n][i*2]=((i&1)?'x':'y');
}
n--;
k-=m/2;
}
if(m%2==1){
for(int i=1;i<=n/2;i++){
a[i*2-1][m]=a[i*2][m]=((i&1)?'x':'y');
}
m--;
}
for(int i=1;i<=n/2;i++){
if(k==0)break;
for(int j=1;j<=m/2;j++){
if(k==0)break;
a[i*2-1][j*2-1]=a[i*2-1][j*2]=(((i+j)&1)?'a':'b');
a[i*2][j*2-1]=a[i*2][j*2]=(((i+j)&1)?'c':'d');
k-=2;
}
}
int top=3;
for(int i=1;i<=n/2;i++){
for(int j=1;j<=m/2;j++){
if(a[i*2][j*2]!='0')continue;
a[i*2-1][j*2]=a[i*2][j*2]=(((i+j)&1)?'a':'b');
a[i*2-1][j*2-1]=a[i*2][j*2-1]=(((i+j)&1)?'c':'d');
top++;
}
}
}
int main(){
int t;
cin>>t;
while(t--){
int n,m,k;
cin>>n>>m>>k;
if(ck(n,m,k)){
cout<<"YES"<<endl;
bt(n,m,k);
pt(n,m);
}
else cout<<"NO"<<endl;
}
return 0;
}
E題 Fixed Points
題目大意:
給定一個長度為 \(n\) 的數組 \(a\) 和一個常數 \(k\)
現在有一個操作,刪除 \(a\) 數組的任意一個,使得右邊的數向左移動一位,即右邊的所有的數的下標 \(-1\)
求最少進行多少次操作能使得最終的數組 \(b\) 中至少有 \(k\) 個數的 \(b[i] = i\)
思路解析:
數據范圍為 \(1≤k≤n≤2×10^3\) ,所以我們很容易想到用 dp 來解決這個問題
狀態定義:\(dp[i][j]\) 表示前 \(i\) 個數中,刪除 \(j\) 個數能得到的 \(b[h] = h(1<=h<=n)\)的最大個數
對於當前的每個 \(a[i]\) 我們有兩種決策:
- 刪除 \(a[i]\),刪除 \(a[i]\) 對其沒有影響,所以可以得到轉移 :\(dp[i][j]=dp[i-1][j-1]\)
- 不刪 \(a[i]\),由於 \(a[i]\) 不刪,所以刪掉的 \(j\) 個就是在i - 1之前刪的,所以 \(a[i]\) 的下標變成了 \(i-j\) ,所以我們可以得到轉移:\(dp[i][j]=dp[i-1][j]+(a[i]==i-j)\)
AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2050;
int a[maxn];
int dp[maxn][maxn];
int main(){
int t;
cin>>t;
while (t--) {
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
if(j==0)dp[i][j]=dp[i-1][j]+(a[i]==i);
else dp[i][j]=max(dp[i-1][j-1],dp[i-1][j]);
}
}
int ans=-1;
for(int i=0;i<=n;i++){
if(dp[n][i]>=k){
ans=i;
break;
}
}
cout<<ans<<endl;
}
return 0;
}