PKU 1011

Sticks
Time Limit Exceedを回避することができず、諦めてカンニング。ShortCoding本に載っていた。著者のサイトにも250Bまでは。書き方は違うけど、やっていることは同じだよなと首を捻ったが、バグで一部枝刈りが働いていなかった。


枝刈り

  • 長い順に試していく
  • 一つの棒の中での順番を長→短に固定(5+1=6は探すが、1+5=6は無視)
  • 全ての棒の長さが等しいので、棒をまるごと入れ替えた解を探さない
  • 同じ長さのパーツは1つのみチェック
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

using namespace std;

vector<int>     part;       //  length of divided sticks
vector<bool>    assign;     //  whether i-th part is assigned
int             total;      //  total length of sticks
int             length;     //  length of a stick

bool            connect( int current, int i );

int main()
{
    int n;
    while ( cin>>n && n>0 )
    {
        part.resize( n );
        assign.resize( n, false );

        for ( int i=0; i<n; i++ )
            cin >> part[i];

        sort( part.begin(), part.end() );

        total = accumulate( part.begin(), part.end(), 0 );
        
        for ( int num=total/part.back(); ; num-- )
        if ( total % num == 0 )
        {
            length = total / num;

            if ( connect( 0, 0 ) )
            {
                cout << length << endl;
                break;
            }
        }
    }

    return 0;
}

//  connect a part to stick
//  current: current total length of sticks
bool connect( int current, int i )
{
    if ( current >= total )
        return true;

    bool first = current % length == 0;
    
    if ( first )
        i = (int)part.size() - 1;

    int last = -1;

    for ( ; i>=0; i-- )
    if ( ! assign[i]  &&
         current/length == (current+part[i]-1)/length  &&
                            //  part must be in one stick
         part[i] != last )  //  check the same-length part only once
    {
        assign[i] = true;
        bool f = connect( current+part[i], i-1 );
        assign[i] = false;

        last = part[i];

        if ( f )  return true;
        if ( first )  break;
    }

    return false;
}