[伯俊]15661番リンクと開始


[伯俊]15661番リンクと開始


質問リンク:https://www.acmicpc.net/problem/15661

質問する


今日はスタート地点を走る人が集まってサッカーをします.サッカーは平日の午後に行われ、義務参加でもない.サッカーをするために集まったのはN人.今からスタートチームとリンクチームに分けます.両チームの人数は違ってもいいですが、1つ以上かかります.
BOJを運営する会社のように、1からNの番号を割り当て、以下の能力値を調べた.コンピテンシー値Sijは、i番とj番が同じチームに属する場合、チームに追加されるコンピテンシー値です.チームのコンピテンシー値は、チーム内のすべてのコンピテンシー値Sijの和です.SijはSjiとは異なり、i番とj番が同じチームに属している場合、チームで増加した能力値はSijとSjiです.
N=4,Sは以下のようになります.

たとえば、1、2番が先頭チーム、3、4番がリンクチームの場合、2つのチームの能力値は次のようになります.
  • チームスタート:S 12+S 21=1+4=5
  • リンクチーム:S 34+S 43=2+5=7
  • 1、3番がスタートチーム、2、4番がリンクチームの場合、両チームの能力値は以下のようになります.
  • スタートチーム:S 13+S 31=2+7=9
  • リンクチーム:S 24+S 42=6+4=10
  • サッカーを面白くするために、スタートチームの能力値とリンクチームの能力値の差を最小限に抑えたい.上記の例のように、1、4が開始グループ、2、3がリンクグループの場合、開始グループのコンピテンシー値は6、リンクグループのコンピテンシー値は6、差異は0であり、この値は最小である.

    入力


    第1行はN(4≦N≦20)を与える.2行目からN行Sを与える.各行はN個の数字で構成され、i行のj番号はSijである.Siiは常に0であり、残りのSijは1以上、100以下の整数である.

    しゅつりょく


    最初の行は、開始グループとリンクグループのコンピテンシー値の差の最大値を出力します.

    問題を理解する


    番号は1からNまでです.コンピテンシー値はsijsjiで異なります.同じチームの全選手対の能力値を加算し、両チームの差が小さい値を見つければよい.
    これは組み合わせの問題です.
    チームごとに数字が違う可能性があるので、2からN/2+1の組み合わせを見つければいいです.あるチームに属さず、別のチームに属するため、N/2+1で捉え、1つのチームであれば最小の差は生じないので、2から捉えます.
    グループを探すなら、該当チームの能力グループを見つければいいです.

    質問へのアクセス

  • 入力します.
  • の組合せが得られる.(2からN/2+1)
  • チームの各グループに対する能力の和を求める
  • 比較最小値
  • 出力
  • コード実装(C++)

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    vector<int>teamStart;
    vector<int>teamLink;
    int check[21] = {false, };
    int N;
    int ability[21][21];
    int minScore = 987654321;
    
    void makeTeam(){
        for(int i = 0 ; i < N ; i++){
            if(check[i]) teamStart.push_back(i);
            else teamLink.push_back(i);
        }
    }
    
    int calAbility(vector<int> &team){
        int score = 0 ;
        for(int i = 0 ; i < team.size() ; i++){
            for(int j = 0 ; j < team.size() ; j++){
                if(i == j) continue;
                score += ability[team[i]][team[j]];
            }
        }
        return score;
    }
    
    void dividePlayer(int idx, int max, int n){
        if(n == max){
            teamStart.clear();
            teamLink.clear();
            makeTeam();
            int startScore = calAbility(teamStart);
            int linkScore = calAbility(teamLink);
            minScore = min(minScore, abs(startScore - linkScore));
        }
        else{
            for(int i = idx ; i < N ; i++){
                if(check[i]) continue;
                check[i] = true;
                dividePlayer(i, max, n+1);
                check[i] = false;
            }
        }
    }
    
    int main(){
        ios_base::sync_with_stdio(false);
        cin >> N;
        for(int i = 0 ; i < N ; i++){
            for(int j = 0 ; j < N ; j++){
                cin >> ability[i][j]; 
            }
        }
        for(int i = 2 ; i < N/2 + 1 ; i++){
            dividePlayer(0,N/2,0);
            
        }
    
        cout << minScore << "\n";
    }

    評価


    グループ化する場合は、現在チェックしているいくつかのインデックスを再帰関数に送信する必要があります.それを忘れてしまうと、デバッグが大変になります.