鏈接:https://codeforces.com/contest/1359
A. Berland Poker
題意:
現在有$n$張牌,$m$張王,共有$k$個人,每個人分得$\frac{k}{n}$張牌,你的得分是你手中王牌的個數減去其余人中擁有最多王牌的個數,求最大得分
思路:
分類討論一下即可,如果$m$大於$\left \lceil \frac{k}{n}\right \rceil$,那么答案就為$\left \lceil \frac{m-\left \lceil \frac{k}{n}\right \rceil}{k-1}\right \rceil$
否則,答案就為$m$

#include<iostream> #include<algorithm> using namespace std; typedef long long ll; int main() { int t; cin>>t; while(t--){ int n,m,k; cin>>n>>m>>k; int num=n/k; if(n%k!=0) num++; if(m>num){ int cnt=(m-num)/(k-1); if((m-num)%(k-1)!=0) cnt++; cout<<num-cnt<<endl; } else cout<<m<<endl; } return 0; }
B. New Theatre Square
題意:
在$n*m$的二維區域內有黑色和白色兩種瓷磚,現在你要覆蓋白色的瓷磚,你可以選擇花費$x$元覆蓋一個瓷磚,也可以選擇花$y$元覆蓋同一行的連續兩個瓷磚,求覆蓋掉所有白色瓷磚的最小花費
思路:
如果$2*x≤y$的話,答案就為$x*num$,$num$為白色瓷磚的個數
否則,就遍歷整個區域,如果$(x,y)$位置是白色瓷磚,那么就觀察$(x,y+1)$處,如果也是白色瓷磚就花費$y$元將兩塊瓷磚同時覆蓋,否則就花$x$元

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e3+5; char a[N][N]; int main(){ int t; cin>>t; while(t--){ ll n,m,x,y,num=0; cin>>n>>m>>x>>y; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cin>>a[i][j]; if(a[i][j]=='.') num++; } if(x*2<=y) cout<<num*x<<endl; else { ll ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(a[i][j]=='.'){ if(j<m&&a[i][j+1]==a[i][j]) ans+=y,j++; else ans+=x; } } cout<<ans<<endl; } } return 0; }
C. Mixing Water
題意:
熱水的溫度為$h$,冷水的溫度為$c$,目標溫度為$t$,現在不停依次倒入熱水,冷水,熱水,冷水...,求倒入幾次后混合水點的溫度最接近$t$(混合水的溫度為倒入的水的溫度總和/倒入的次數)
思路:
可以發現倒入冷水后的溫度始終都為$(h+c)/2$,所以答案不可能為除$2$以外的偶數,所以當$t≤(h+c)/2$時,答案一定為$2$
再觀察倒入熱水后的規律,發現倒入熱水之后,溫度的函數為$(x*h+h+x*c)/(2*x+1)$($x$是倒入了幾次熱水,並且從$0$開始),而且這個函數是以遞減變化的
因此我們可以二分倒入熱水的次數,找到最后一次水的溫度大於等於$t$的時刻$tim$,再與$tim+1$時刻進行比較看哪個值更接近$t$,就能求出需要倒幾次熱水

#include<bits/stdc++.h> #define ll long long using namespace std; long double h,c,t,sb; long double pd(int x){ return (h*x+h+c*x)/(2*x+1); } int main(){ ll p; cin>>p; while(p--){ cin>>h>>c>>t; sb=(h+c)/2; if(sb>=t) cout<<2<<endl; else{ int l=0,r=1e9; while(l<r){ ll mid=(r+l+1)/2; if(pd(mid)>=t) l=mid; else r=mid-1; } if(fabs(pd(r+1)-t)<fabs(pd(r)-t)) r++; cout<<2*r+1<<endl; } } return 0; }
D. Yet Another Yet Another Task
題意:
給一個序列,你可以選擇一個子段,要求去掉子段最大值后的和最大,求出這個最大值
思路:
可以發現$-30≤n≤30$,那么我們可以在$1-30$的范圍內枚舉區間的最大值,因為如果當區間最大值為負數或$0$時,區間的其他數肯定也為負數或者為$0$,那么選擇這樣的一段是沒有意義的,最大值肯定為$0$
在枚舉區間最大值時,我們記錄下(最大子段和-枚舉值)的最大值,遇到比枚舉值大的數的時候,就將累加的數清零即可

#include<iostream> #include<algorithm> using namespace std; const int maxn=1e5+10; int a[maxn]; int main() { int n,flag=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=0,cnt=0; for(int i=1;i<=30;i++){ cnt=0; for(int j=1;j<=n;j++){ if(a[j]>i){ cnt=0; continue; } cnt+=a[j]; if(cnt<0) cnt=0; ans=max(cnt-i,ans); } } cout<<ans; return 0; }