ABC057

過去問をC#で解いていく・・・

コードの中にコメント書いてく形式にしてみました。

A問題

画像1

using System;

class Program{
 public static void Main(){

   var input = Console.ReadLine().Split(' ');
   int a = int.Parse(input[0]);
   int b = int.Parse(input[1]);
   
   int ans = a + b;
   if(ans >= 24) ans -= 24;

   Console.WriteLine(ans);
 }
}

B問題

画像4

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Math;

class Program{
 public static void Main(){

   //入力
   var nm = Console.ReadLine().Split();
   int n = int.Parse(nm[0]);
   int m = int.Parse(nm[1]);
   
   int[] a = new int[n];
   int[] b = new int[n];
   int[] c = new int[m];
   int[] d = new int[m];
   
   for(int i=0; i<n; i++){
     var ab = Console.ReadLine().Split();
     a[i] = int.Parse(ab[0]);
     b[i] = int.Parse(ab[1]);
   }
   for(int i=0; i<m; i++){
     var cd = Console.ReadLine().Split();
     c[i] = int.Parse(cd[0]);
     d[i] = int.Parse(cd[1]);
   }
   
   //処理
   for(int i=0; i<n; i++){
     //1番目のチェックポイントを初期値に設定
     int min = Math.Abs(a[i]-c[0]) + Math.Abs(b[i]-d[0]);;
     int ans = 0;
     
     //2番目以降のチェックポイントとの比較
     for(int j=1; j<m; j++){
       int l = Math.Abs(a[i]-c[j]) + Math.Abs(b[i]-d[j]);
       
       //より近いチェックポイントがあれば入れ替える
       if(l < min){
         min = l;
         ans = j;
       }
     }
     Console.WriteLine(ans+1);
   }
 }
}

C問題

画像4

Nが10^10なので、普通にループを回すと間に合わない。

A<Bとしたとき、Nの平方根をSQRTは、A<=SQRT<=Bとなるはず(証明はしません)。このとき、Aは10^5以下になるから、平方根をとった後、先にAを決定して、そこから大きいほうのBの桁数を求めればOK!

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Math;

class Program{
 public static void Main(){

   //入力
   long n = long.Parse(Console.ReadLine());
   
   //処理
   long a = 0 , b = 0;
   //nの平方根を求める
	long m = (long)Sqrt(n);
   //mから割り切れる数を探して、F(a,b)が最小となる組み合わせを探す
   for(long i=m; i>0; i--){
     if(n % i == 0){
       a = i;
       b = n / a;
       break;
     }
   }
   
   //桁数を求める
   int ans = 0;
   while(b>0){
     b /= 10;
     ans++;
   }
   
   Console.WriteLine(ans);
 }
}

D問題

画像4

まず、選んだ品物の価値の平均が最大になるのは、価値が大きいほうからA個選べばいい。ポイントとなるのが、大きいほうからA個目と同じ価値の品物(Vaとする)が複数あった場合。複数あるうちから品物の数の合計がA個以上、B個以下になるように、Vaの品物を選択する。

n個からk個を選択するとき、nCkの計算が必要になるが、nCkの値はパスカルの三角形と対応するらしい。DPでパスカルの三角形のグラフを作成し、そこから必要となるnCkの値を使って答えを求める。

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Math;

class Program{
 public static void Main(){

   //入力
   var input = Console.ReadLine().Split();
   int n = int.Parse(input[0]);
   int a = int.Parse(input[1]);
   int b = int.Parse(input[2]);
   
   var input2 = Console.ReadLine().Split();
   long[] v = new long[n];
   for(int i=0; i<n; i++){
     v[i] = long.Parse(input2[i]);
   }
   
   //処理
   
   //降順に並べ替え
   Array.Sort(v);
   Array.Reverse(v);
   
   //解答用の変数の宣言
   double ave = 0;
   long num = 0;
   
   //一番大きい数とA番目に大きい数が同じ場合
   if(v[0] == v[a-1]){
     ave = v[0];
     
     //同じ要素の個数を数える
     int cnt = 0;
     for(int i=0; i<n; i++){
       if(v[0] != v[i]) break;
       else cnt++;
     }
     
     //dpでパスカルの三角形をつくる
     long[,] dp = new long[cnt+1,cnt+1];
     dp[0,0] = 1;
    for(int i=1; i<=cnt; i++){
      for(int k=0; k<=i; k++){
       if(k >= 1){
         dp[i,k] = dp[i-1,k-1] + dp[i-1,k];
       }
       else {
         dp[i,k] = dp[i-1,k];
       }
      }
    }
     
     int sml = Min(b,cnt);
     for(int i=a; i<=sml; i++){
       num += dp[cnt,i];
     }
   }

   
   //一番大きい要素とA番目に大きい数が異なる場合
   else{
     long wa = 0;
     for(int i=0; i<a; i++){
       wa += v[i];
     }
     ave = (double)wa / a;
     
     //v[a-1]と同じ値の要素数を数える
     bool flg = false;
     int cnt = 0;
     int index = 0;
     
     for(int i=0; i<n; i++){
       if(v[i] == v[a-1]){
         if(!flg){
           index = i;//同じ値の要素番号をメモ
           flg = true;
         }
         cnt++;
       }
       if(flg == true && v[i] != v[a-1]){
         break;
       }
     }
     
     //dpでパスカルの三角形をつくる
     long[,] dp = new long[cnt+1,cnt+1];
     dp[0,0] = 1;
    for(int i=1; i<=cnt; i++){
      for(int k=0; k<=i; k++){
       if(k >= 1){
         dp[i,k] = dp[i-1,k-1] + dp[i-1,k];
       }
       else {
         dp[i,k] = dp[i-1,k];
       }
      }
    }
     
     num = dp[cnt,a-index];
   }
   
   Console.WriteLine(ave);
   Console.WriteLine(num);
 }
}

パスカルの三角形、2か所で作っていて見栄え悪いですが、悪しからず。問題そのものというよりも、型の変換のエラーにかなり時間がかかった。あとC#の二次元配列の要素、[x,y]こんな感じで指定するのね。

今回の問題を機に、nCkについて別記事でまとめようと思う。調べていると問題としては、nCk(mod p)のほうが出題率は高いみたいなので、そっちもまとめたい。(結構理解大変だった)