题解/算法 {E. Breakfast II}

题解/算法 {E. Breakfast II}

@LINK: https://codeforces.com/gym/105184;

首先 不要一看到涉及到几何 就以为就是几何算法, 不是的…

通过N,M, b,c的值, 你可以得到一个Need 表示 所有人需要去食堂的次数;
每个人 预处理一个Cost[a][b] 表示a这个人 去b个食堂 且到达办公室的 最小代价 (1<=b <= 3), 如果b==0表示 这个人待在原地;
. 比如b==2, 那么就暴力枚举 他的所有路径可能 我们用0,1,2 表示这3个食堂; S为宿舍, T为办公室, 则枚举全排列S -> {[01] [10] [02] [20] [12] [20]} -> T;

然后就可以用DP了, 这些人 每个人有4种可能{0: 原地; 1: 去一次食堂; 2: 去两次; 3: 去三次}, DP求一个DP[I][J] 表示[0...I]这些人 他们去了J次食堂的 最小代价;

int Need;
vector< pair<int,int> > Store(3);
pair<int,int> Target;
vector< pair<int,int> > Person;
int N;
{
    int n, m; cin>> n>> m>> N;
    int b, c; cin>> b>> c;
    Need = std::max( (n+b-1)/b, (m+c-1)/c);
    FOR_( i, 0, 2){
        cin>> Store[i].first>> Store[i].second;
    }
    cin>> Target.first>> Target.second;
    Person.resize( N);
    for( auto & i : Person){
        cin>> i.first>> i.second;
    }
}

auto GetDist = []( std::pair<int,int> & _a, std::pair<int,int> & _b){
    int dx = std::abs(_a.first-_b.first);  dx *= dx;
    int dy = std::abs(_a.second-_b.second);  dy *= dy;
    return std::sqrt( ___Double_( dx + dy));
};

static ___Double_ Cost[ 1003][ 4];
FOR_( i, 0, N-1){
    Cost[i][0] = 0;
    FOR_( j, 1, 3){
        Cost[ i][ j] = 1e11;
    }
}
FOR_( p, 0, N-1){
    FOR_( st, 1, 0b111){
        std::vector<int> permu;
        FOR_( bit, 0, 2){
            if( (st>> bit) & 1){ permu.emplace_back( bit);}
        }
        do{
            ___Double_ cost = 0;
            std::pair<int,int> pre = Person[p];
            for( auto m : permu){
                cost += GetDist( pre, Store[m]);
                pre = Store[m];
            }
            cost += GetDist( pre, Target);
            {
                auto & update = Cost[ p][ __builtin_popcount(st)];
                update = std::min( update, cost);
            }
        }while( std::next_permutation( permu.begin(), permu.end()));
    }
}

___Double_ DP[ 1003][ 1003];

FOR_( I, 0, N-1){
    if( I == 0){
        FOR_( J, 0, Need){ DP[I][J] = 1e11;}
        FOR_( J, 0, 3){ DP[I][J] = Cost[I][J];}
        continue;
    }
    std::memcpy( DP[I], DP[I-1], sizeof(DP[I]));
    FOR_( J, 0, Need){
        FOR_( cont, 1, 3){
            auto const& preDP = DP[ I - 1][ J];
            if( preDP > 1e10){ continue;}
            if( J + cont > Need){ continue;}
            DP[I][ J + cont] = std::min( DP[I][ J + cont], preDP + Cost[I][cont]);
        }
    }
}

if( DP[N-1][Need] > 1e10){
    ASSERT_(0);
}
cout<< DP[N-1][Need];
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值