洛谷P2066 机器分配【dp】
题目链接:https://www.luogu.com.cn/problem/P2066
题目描述
总公司拥有高效设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。
输入格式
第一行有两个数,第一个数是分公司数N,第二个数是设备台数M。
接下来是一个N*M的矩阵,表明了第 I个公司分配 J台机器的盈利。
输出格式
第1行为最大盈利值
第2到第n为第i分公司分x台
P.S.要求答案的字典序最小
输入输出样例
输入 #1
3 3
30 40 50
20 30 50
20 25 30
输出 #1
70
1 1
2 1
3 1
先不考虑字典序最小的问题,代码很好写。就是个背包
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e3;
int n,a[maxn][maxn],f[maxn][maxn],ans[maxn][maxn];
void print(int n, int m){//用于输出前n个公司,分配m台机器的方案
if(n == 0) return;
else{
print(n-1, m-ans[n][m]);//往前递归
printf("%d %d
", n, ans[n][m]);//输出公司编号和设备数
}
}
int main(){
int n,m; scanf("%d%d",&n, &m);
for(int i=1; i<=n; i++)for(int j=1; j<=m; j++)scanf("%d", &a[i][j]);//第i个公司分配j台机器的盈利。
for(int i=1; i<=n; i++)//公司
for(int j=1; j<=m; j++)//总共分配的机器数
for(int k=0; k<=j; k++)//给当前公司分配几个设备
if(f[i][j]<=f[i-1][j-k]+a[i][k]){
f[i][j] = f[i-1][j-k]+a[i][k];
ans[i][j] = k;//记录一下方案,前i个公司分配j台机器时,公司i获得的设备数
}
printf("%d
", f[n][m]);
print(n,m);
return 0;
}
看这组数据:
2 15
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
刚才的程序输出:
2
1 1
2 1
但字典序最小的方案是:
2
1 0
2 15
若考虑上字典序呢,我们就让编号靠后的公司分配的机器多一点,这样前面分配的机器就少,字典序就小
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e3;
int n,a[maxn][maxn],f[maxn][maxn],ans[maxn][maxn];
void print(int n, int m){
if(n == 0) return;
else{
print(n-1, m-ans[n][m]);
printf("%d %d
", n, ans[n][m]);
}
}
int main(){
int n,m; scanf("%d%d",&n, &m);
for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d", &a[i][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
for(int k=0; k<=j; k++)
if(f[i][j]<=f[i-1][j-k]+a[i][k]){//只有这不同,小于等于
f[i][j] = f[i-1][j-k]+a[i][k];
ans[i][j] = k;
}
printf("%d
", f[n][m]);
print(n,m);
return 0;
}
为什么改成小于等于就对了?如果我们在等于时也更新一下ansi j= k;
就可以保证这个k是最优状态里最大的,也就保证最终得到的方案字典序最小
还有一种写法,就是倒序枚举i,j,k,这样一开始记录的k值就是最大的