题意:给出n~100为仓库的数量, 给出m~30为有守夜人的数量, 然后给出m个数值,为对应守夜人应付的酬劳,每个守夜人的能力与他需要的酬劳是相等的,并且守夜人可以同时负责多个仓库的安全,不过这样子安全值就变为val[i]/k(val[i]表示第i个守夜人的能力值,k表示他负责的仓库数量, /为取int整),先在要的出方案,先使得所有仓库中安全值最低的那个仓库的安全值越高, 并且要使得酬劳越低。
题解:100个仓库很小,dp【i】表示搞定i个并且最小(先不考虑钱),然后dp完得到结果。再来一个dp,dp【i】表示搞定i个并且都大于ans并且钱最少的。
重点:根据问法,两次dp。还有可以一个dp,把安全值和钱和一个int表示,只是wa了。。。不知道问啥。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int maxn = 200 + 10;
const int INF = INT_MAX/2 - 1;
const int key = 1e5;
int dp[maxn], p[maxn], n, m;
int getAns(int a, int b)//这个函数没用
{
int aa = a/key, bb = b/key;
if(aa == bb)
{
if(a < b)
return a;
else
return b;
}
if(a > b)
return a;
return b;
}
void getDp()//找到最大值
{
CLR(dp);
dp[0] = 2000*key;
REP_D(i, 1, m)
{
for(int j = n;j >= 0;j--)//注意01背包
{
for(int k = 1;k <= p[i];k++)
{
int val = p[i]/k;
if(j >= k)
{
dp[j] = max(dp[j], min(dp[j-k], val*key) );
}
}
}
}
}
int gao(int ans)//已知最大值,求钱
{
if(ans == 0)
return 0;
for(int i = 1;i <= n;i++)
{
dp[i] = INF;
}
dp[0] = 0;
for(int i = 1;i <= m;i++)
{
for(int j = n;j >= 0;j--)
{
int k = p[i]/ans;
k = min(k, j);
if(j >= k&&dp[j-k]!=INF)
{
dp[j] = min(dp[j], dp[j - k] + p[i]);
}
}
}
return dp[n];
}
void solve()
{
getDp();
int ans = dp[n];
ans /= key;
int ans_2 = gao(ans);
printf("%d %d\n", ans, ans_2);
}
int main()
{
// freopen("5Ein.txt", "r", stdin);
//freopen("5Eout.txt", "w", stdout);
while(scanf("%d%d", &n, &m) != EOF)
{
if(!n && !m)
break;
REP_D(i, 1, m)
{
scanf("%d", &p[i]);
}
solve();
}
return 0;
}