UVA-10163 Storage Keepers DP

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1104

题意:

  有n个仓库(最多100个),m个管理员(最多30个),每个管理员有一个能力值P(接下来的一行有m个数,表示每个管理员的能力值).每个仓库只能由一个管理员看管,但是每个管理员可以看管k个仓库(但是这个仓库分配到的安全值只有p/k,其中p/k取整数部分), 每个月公司都要给看管员工资,雇用的管理员的工资即为他们的能力值p和,问,使每个仓库的安全值最高的前提下,使的工资总和最小。

输出最大安全值,并且输出最少的花费。

 

题解:

  令dp[i][j]==x表示前i个人看管j个仓库时, 能获得的最大安全总值为x.  或者你也可以二分求解

  第二次DP过程, 求在最大安全总值==L的情况下, 总花费最小.

       令dp[i][j]==x表示前i个人看管j个仓库且最大安全总值==L时, 最小花费为x.

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define pii pair<int,int>
#define MP make_pair
typedef long long LL;
const long long INF = 1e17+1LL;
const double Pi = acos(-1.0);
const int N = 200+10, M = 1e3+20, mod = 1e9+7, inf = 2e9+10;


int dp1[N][N],dp2[N][N],n,m,p[N];
void solve() {
    for(int i = 0; i <= m; ++i)
        for(int j = 0; j <= n; ++j) dp1[i][j] = inf;
    for(int i = 1; i <= m; ++i) {
        for(int j = 1; j <= n; ++j) {
            dp1[i][j] = dp1[i-1][j];
            if(i == 1) dp1[i][j] = p[i] / j;
            else  dp1[i][j] = dp1[i-1][j];
            for(int k = 0; k < j; ++k) {
                if(i!=1)dp1[i][j] = max(dp1[i][j],min(dp1[i-1][k],p[i]/(j-k)));
            }
        }
    }

    for(int i = 0; i <= m; ++i)
        for(int j = 0; j <= n; ++j) dp2[i][j] = 0;
    for(int i = 1; i <= m; ++i) {
        for(int j = 1; j <= n; ++j) {
            if(i == 1) dp2[i][j] = (p[i]/j)>=dp1[m][n]?p[i]:inf;
            else dp2[i][j] = dp2[i-1][j];
            if(i != 1)
            for(int k = 0; k < j; ++k) {
                if(p[i] / (j-k) >= dp1[m][n])
                    dp2[i][j] = min(dp2[i][j],dp2[i-1][k]+p[i]);
            }
        }
    }
    if(dp1[m][n] == 0) cout<<"0 0"<<endl;else
    cout<<dp1[m][n]<<" "<<dp2[m][n]<<endl;
}
int main() {
    while(scanf("%d%d",&n,&m)!=EOF) {
        if(n == 0 && m == 0) break;
        for(int i = 1; i <= m; ++i) scanf("%d",&p[i]);
        solve();
    }
    return 0;
}

 

posted @ 2017-03-05 18:23  meekyan  阅读(188)  评论(0编辑  收藏  举报