公式

A - Full House 2 解説 by physics0523


初心者の方へ

解法1. カードを \(1\) 枚追加することを試す

追加するカードとして \(1,2,\dots,13\) のみ考えればよいです。
これを全通り試すことでこの問題に正解できます。
なお、フルハウスかどうかの判定は ABC263-A にて既出です (後述の解法を応用して判定可能です)。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

bool isFullHouse(int a,int b,int c,int d,int e){
  vector<int> card={a,b,c,d,e};
  sort(card.begin(),card.end());
  if(
    card[0]==card[1] && card[1]==card[2] &&
    card[2]!=card[3] &&
    card[3]==card[4]
  ){
    return true;
  }
  if(
    card[0]==card[1] &&
    card[1]!=card[2] &&
    card[2]==card[3] && card[3]==card[4]
  ){
    return true;
  }
  return false;
}

int main(){
  int a,b,c,d;
  cin >> a >> b >> c >> d;
  for(int e=1;e<=13;e++){
    if(isFullHouse(a,b,c,d,e)){
      cout << "Yes\n";
      return 0;
    }
  }
  cout << "No\n";
  return 0;
}

問題条件の言い換え

実は、あと \(1\) 枚加えてフルハウスとなる組は以下の条件のどちらかを満たすものに限られます。

  • 同じ数が書かれたカード \(3\) 枚と、別の同じ数が書かれたカード \(1\) 枚からなる。
  • 同じ数が書かれたカード \(2\) 枚と、別の同じ数が書かれたカード \(2\) 枚からなる。

証明は、フルハウスから \(1\) 枚除くことを考えると直ちに導かれます。

解法2. ソートして判定

ソートした \(4\) 枚のカードを \(A_1,A_2,A_3,A_4\) とします。

  • 同じ数が書かれたカード \(3\) 枚と、別の同じ数が書かれたカード \(1\) 枚からなる。

この条件は、以下の条件のどちらかを満たすことと同値です。

  • \(A_1 = A_2 = A_3 \neq A_4\)

  • \(A_1 \neq A_2 = A_3 = A_4\)

  • 同じ数が書かれたカード \(2\) 枚と、別の同じ数が書かれたカード \(2\) 枚からなる。

この条件は、以下の条件のどちらかを満たすことと同値です。

  • \(A_1 = A_2 \neq A_3 = A_4\)

これを利用するとこの問題に正解できます。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  vector<int> a(4);
  cin >> a[0] >> a[1] >> a[2] >> a[3];
  sort(a.begin(),a.end());
  if(a[0]==a[1] && a[1]==a[2] && a[2]!=a[3]){
    cout << "Yes\n";
    return 0;
  }
  if(a[0]!=a[1] && a[1]==a[2] && a[2]==a[3]){
    cout << "Yes\n";
    return 0;
  }
  if(a[0]==a[1] && a[1]!=a[2] && a[2]==a[3]){
    cout << "Yes\n";
    return 0;
  }
  cout << "No\n";
  return 0;
}

この解法をよく観察すると、更に次の通りに変形できます。

  • \(A_1 \neq A_2, A_2 \neq A_3, A_3 \neq A_4\) のうち丁度 \(1\) つが満たされる時に限り、答えは Yes

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  vector<int> a(4);
  cin >> a[0] >> a[1] >> a[2] >> a[3];
  sort(a.begin(),a.end());
  int c=0;
  if(a[0]!=a[1]){c++;}
  if(a[1]!=a[2]){c++;}
  if(a[2]!=a[3]){c++;}
  if(c==1){cout << "Yes\n";}
  else{cout << "No\n";}
  return 0;
}

解法3. バケットソートの要領で判定

言い換えた条件を、バケットソートの要領で判定します。実装例では、 C++ の map を活用しています。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  map<int,int> m1,m2;
  for(int i=0;i<4;i++){
    int a;
    cin >> a;
    m1[a]++;
  }
  for(auto &nx : m1){
    m2[nx.second]++;
  }
  if(m2[1]==1 && m2[3]==1){
    cout << "Yes\n";
  }
  else if(m2[2]==2){
    cout << "Yes\n";
  }
  else{
    cout << "No\n";
  }
  return 0;
}


おまけ

実は、 \(4\) 枚のカードをどの種類に振り分けるかは以下の \(5\) 通りに限られ、この \(5\) 通りはサンプルで尽くされています。

  • \((1,1,1,1)\)
  • \((2,1,1)\)
  • \((2,2)\)
  • \((3,1)\)
  • \((4)\)

このような振り分けの個数は、 分割数 というトピックで知られています。

投稿日時:
最終更新: