CF Round #522 (Div. 2)
ACM題集:https://blog.csdn.net/weixin_39778570/article/details/83187443
題目連結:http://codeforces.com/contest/1079
官方題解:http://codeforces.com/blog/entry/63324
A題
題意:吃一種食物需要完整的一套餐具,每套餐具是是一樣的,由不同的餐具組成,如刀子,叉子,筷子(由不同的數字表示,如1,2,3)。用餐過程會被偷掉一些餐具。現在知道最後剩下n個餐具,分別為a[1],a[2],a[3]…a[n],和k個客人,問最少被偷掉幾個餐具
解法:最主要的點是算出食物的數目(fish),跟據餐具的套數算出最少的fish
fish的最少為 餐具套數/人數 (向上取整)
(sum為一套餐具最少有幾種餐具)
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,k,a[200];
int main(){
cin>>n>>k;
int x;
fo(i,1,n){
scanf("%d",&x);
a[x]++;
}
int mx= -1,sum=0;
fo(i,1,100){
if(a[i]) mx = max(mx,a[i]),sum++;
}
if(mx%k==0)mx/=k;
else mx = mx/k +1;
int ans = sum*mx*k - n;
cout<<ans;
return 0;
}
B題
題意:問一個字串最少能分成幾行,每列最多20個字元。形成一個矩陣,不為矩陣由*代替,兩行差不能超過2個星
解法:算出最少有多少行,跟據行算出列,算出有多少個星星,輸出,每行最多一個星星
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
char s[106];
int main(){
scanf("%s",s+1);
int len = strlen(s+1);
int row = len%20==0? len/20:len/20+1;
int col = len%row==0? len/row:len/row+1;
int xin = row * col - len;
int t1 = 0, t2=1;
cout<<row<<" "<<col<<endl;
fo(i,1,row){
int j = 1;
if(t1++<xin)printf("*"),j++;
for(j; j<=col; j++){
printf("%c",s[t2++]);
}
printf("\n");
}
}
C題
題意:給定一個數列a,求數列b,
解法:
記憶化搜尋,O(5n)
表示第
步的值為
,
步的值為$i $
表示第
步的值為x是否合法,合法返回
的值
#include<bits/stdc++.h>
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,dp[100005][6],a[100005];
// dp[step][i]=x 表示第step-1步的值為x,step步的值為i
// dfs(step,x) 表示第step-1步的值為x是否合法,合法返回step-1的值
bool dfs(int step, int x){// step當前,x屬於step-1
if(step>n) return dp[step][0]=x;
if(dp[step][x]!=-1)return dp[step][x];
if(a[step]>a[step-1]){
fo(i,x+1,5){
if(dfs(step+1,i))return dp[step][i]=x; // 搜對了才賦值,相當於回溯,但顯然比回溯快
} // 因為回溯會破壞掉之前已經搜尋過的錯誤解空間,導致重判
}else if(a[step]<a[step-1]){
fo(i,1,x-1){
if(dfs(step+1,i))return dp[step][i]=x;
}
}else{
fo(i,1,5)if(i!=x){
if(dfs(step+1,i))return dp[step][i]=x;
}
}
return dp[step][x]=0; // 上個值x搞錯了,往後都不合法
}
int b[100005];
int main(){
cin>>n;
fo(i,1,n)scanf("%d",&a[i]);
memset(dp,-1,sizeof(dp));
dfs(1,0); // 出口總返回0, 第0號元素的值
if(dp[n+1][0]!=-1){
int t=n;
for(int x=dp[n+1][0],i=n; i>=1; x=dp[i][x],i--){
b[t--]=x; // 往回賦值
}
fo(i,1,n)printf("%d%c",b[i],i==n?'\n':' ');
return 0;
}
puts("-1");
return 0;
}
D題
題意:給一個網格上的兩個點a,b和一條直線 Ax+By+C=0。點a只能沿著網格或這直線走到b。問最小行走距離。
解法:列舉a不走直線到b。
a沿x軸走到直線到達點a1,a沿y軸走到直線到達點2
同理得b1,b2.
列舉 a --> a(1or2) --> b(1or2) --> b 的距離
取5種情況的最小值
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
double a,b,c,x1,y1,x2,y2,xa[6],ya[6],xb[6],yb[6];
double dist(double x1,double y1,double x2, double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main(){
cin>>a>>b>>c;
cin>>x1>>y1>>x2>>y2;
xa[1]=x1,ya[1]=-(a*xa[1]+c)/b; // a上下
ya[2]=y1,xa[2]=-(b*ya[2]+c)/a; // a左右
xb[1]=x2,yb[1]=-(a*xb[1]+c)/b; // b上下
yb[2]=y2,xb[2]=-(b*yb[2]+c)/a; // b上下
double ans = fabs(x1-x2)+fabs(y1-y2);
fo(i,1,2)fo(j,1,2){
ans = min(ans, fabs(xa[i]-x1)+fabs(ya[i]-y1)+fabs(xb[j]-x2)+fabs(yb[j]-y2)+dist(xa[i],ya[i],xb[j],yb[j]));
}
printf("%.10f\n",ans);
return 0;
}
E題
題意:把一種質量看做一種商品。給定一些商品的重量即數列a[1],[2],a[3]…a[n],但是同學1無法區分重量和商品,但是他可以問同學2:k個商品的重量和sum有哪些。同學2會指出哪k個商品和為sum(如果存在的話)。問同學1最多能區分多少個商品,也就是說具體知道哪些商品的重量。
解法:由於同學1太傻了,所以同學2一但指出k個商品但是商品中有不同種類的,那麼同學1還是無法得知哪個商品具體是哪個。
那麼同學1只能詢問同一種商品,k個了。
表示i個商品組成質量為j的組合數。
這裡的組合數是這樣的
如數
被算為 2 4 6 8 共4種組合 ,而不是01揹包那樣, 4 可以由不同的2組成,然後進行多次計算
即