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; }