1171. 【动态规划】机器分配
题目描述
总公司拥有高效设备 M 台,准备分给下属的 N 个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这 M 台设备才能使国家得到的盈利最大?求出最大盈利值。其中 M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数 M。
输入
第一行有两个数,第一个数是分公司数 N,第二个数是设备台数 M。一个 N*M 的矩阵,表明了第 I 个公司分配 J 台机器的盈利。
输出
第一行为最大盈利值,接下来 n 行,每行第一个数为公司编号,第二个数为公司分配的机器数。
若题目存在多解,编号越大的公司应优先分得越多的机器。
样例数据
输入 #1
3 3 30 40 50 20 30 50 20 25 30
输出 #1
70 1 1 2 1 3 1
1.动态规划解法
一道经典的区间DP练习题。
设f[i][j]为前i个公司总共分配j台机器的最大利润。对于第i家子公司,我们可以给其分配的机器台数为:
0,1,2……m
所以在该区间内枚举一个值k,状态转移方程即为:
f[i][j]=max(f[i-1][j-k],f[i][j]);
那么,如何处理方案输出问题呢?
我们设path[i][j][h]对于前i个公司共分配j台机器的最优方案,第h个公司应分配多少台机器,当状态发生转移时,更新path数组即可。最终的答案就存放在path[n][m][i]之中。
贴代码:
#include<bits/stdc++.h>
using namespace std;
int f[11][16],graph[11][16],path[11][16][11],n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cin>>graph[i][j];
}
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=j;k++)
{
if (f[i][j]<f[i-1][j-k]+graph[i][k])
{
f[i][j]=f[i-1][j-k]+graph[i][k];
for(int h=1;h<i;h++) path[i][j][h]=path[i-1][j-k][h];//path数组只有在状态发生转移时才更新
path[i][j][i]=k;
}
}
cout<<f[n][m]<<endl;
for(int i=1;i<=n;i++) cout<<i<<" "<<path[n][m][i]<<endl;
return 0;
}
如果你依照上述思想写出了dp程序并提交,恭喜你,只有90分。
#那么这是为什么呢?
回到题面,我们会发现小小的一行字,人畜无害的样子:
P.S.要求答案的字典序最小
你会发现如果这样做,方案输出是错的。
如何使字典序最小呢?这需要我们倒着枚举。
设表示的意思为“不给”第i家公司k台机器(k的值域同上),那么状态转移方程需改为:
f[i][j]=max(f[i][k],f[i-1][k]+graph[i][j-k]);
再根据这个,对于path数组的更新操作进行一些微调,即可得到满分程序了:
#include<bits/stdc++.h>
using namespace std;
int f[11][16],graph[11][16],path[11][16][11],n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cin>>graph[i][j];
}
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=j;k++)
{
if (f[i][j]<f[i-1][k]+graph[i][j-k])
{
f[i][j]=f[i-1][k]+graph[i][j-k];
for(int h=1;h<i;h++) path[i][j][h]=path[i-1][k][h];
path[i][j][i]=j-k;//因为改为了“不给”第i家公司k台机器,所以必须如此调整
}
}
cout<<f[n][m]<<endl;
for(int i=1;i<=n;i++) cout<<i<<" "<<path[n][m][i]<<endl;
return 0;
}
搜索出奇迹
DFS保证找到最大答案的时候就是字典序最少的,因为我从1号-n号枚举用的多少机器,用的机器数量也是由少到多。当最后得到答案相等的情况下就不用需要比较字典序了,直接return,只有碰到大小不一的时候才更新答案机器数
#include<bits/stdc++.h>
using namespace std;
int n,m,a[20][20],pau[20],f[20],ans;//f[i]是答案机器数,pau是当前假设的机器数量
void dfs(int Nnum,int Nans,int Nm) {//Nnum是现在的公司编号,Nans是现在的盈利,Nm是剩余的机器
if(Nm<0) return;
if(Nnum==n+1) {
if(Nans>ans) {
ans=Nans;
for(int i=1;i<=n;i++) f[i]=pau[i];
}
return;
}
for(int i=0; i<=m; i++) pau[Nnum]=i,dfs(Nnum+1,Nans+a[Nnum][i],Nm-i);//i枚举这个公司用多少台机器
return;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
scanf("%d",&a[i][j]);
dfs(1,0,m);
printf("%d\n",ans);
for(int i=1; i<=n; i++) printf("%d %d\n",i,f[i]);
return 0;
}
更多推荐


所有评论(0)