【题目链接】信息学奥赛一本通1266:【例9.10】机器分配
【题目补充】在输出分公司编号和该分公司获得设备台数时,要求以字典序最小的方案输出。也就是说,在盈利值相等的情况下,选择输出编号越小的公司分配数量越小。
【题解思路】
1.动态规划
状态表示:f[ i ][ j ]表示前 i 个公司分配 j 个设备的最大盈利值。
需要输出的最大盈利值为f[ n ][ m ].
状态计算:当从前 i-1 个公司转移到前 i 个公司时,对于第 i 个公司可以分配0,1,2,3,...,m-1,m个设备。可以理解为背包问题。
当第 i 个公司分配0个设备时,f[ i ][ j ]=f[ i-1 ][ j ] ;
当第 i 个公司分配1个设备时,f[ i ][ j ]=f[ i-1 ][ j-1 ] + a[ i ][ 1 ];
以此类推。
可得第i个公司分配k个设备的状态转移方程为 f[ i ][ j ]=max( f[ i ][ j ] , f[ i-1 ][ j-k ]+a[ i ][ k ])。
2.如何求分配情况
num[ i ][ j ]表示前 i 个公司共同分配 j 个设备时,第 i 个公司分配到的数量。
可以用递归函数实现
void putout(int a,int b) //前 a 个公司分配 b 个设备的情况
{
if(a>1) putout(a-1,b-num[a][b]);
cout<<a<<" "<<num[a][b]<<endl;
}
注意,这里递归的过程是逆序的,从前n个一直到前1个结束,所以cout要写在递归后。
【代码】
#include<iostream>
using namespace std;
const int N=20;
int n,m;
int a[N][N];
int f[N][N]; //f[i][j]:前 i 个公司分配 j 个设备的最大盈利
int num[N][N]; //num[i][j]:前 i 个公司共同分配 j 个设备时,第 i 个公司分配到的数量
void putout(int a,int b) //前 a 个公司分配 b 个设备的情况
{
if(a>1) putout(a-1,b-num[a][b]);
cout<<a<<" "<<num[a][b]<<endl;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=0;k<=j;k++) //第 i 个公司分配 k 个,可以是 0个
{
if(f[i-1][j-k]+a[i][k]>=f[i][j])
{
f[i][j]=f[i-1][j-k]+a[i][k];
num[i][j]=k;
}
}
}
}
cout<<f[n][m]<<endl;
putout(n,m);
return 0;
}