P2066 机器分配 (DP+DP输出)

题目描述

总公司拥有高效设备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
分析:dp(i,j)表示前I个公司一共选J台机器的最优答案,那转移方程为dp(i,j)=max(dp(i-1,j-k);这个方程表示如果我们第I选择了J台,那应该是从i-1次中选择K台才得出的答案;
那么如何输出字典序最小呢?我们可以使用pa(i,j,h) 表示前i个公司一共选j台机器,h号公司选了多少台;
#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;
}

但是!!!!!!!这并不可以AC 因为还有个限制条件那就是字典序最小,我们怎么解决呢??

按上面这种方式的枚举,我们都是尽可能的选满填充所以得出的字典序可能是最大的,那我们可以尽可能的不选满,那就是意味着我们要倒叙的枚举

#include<stdio.h>
#include<string.h>
int mo[20][20],dp[20][20],pa[20][20][20];
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",&mo[i][j]);
    memset(dp,0,sizeof(dp));
    for(int i=1 ; i<=n ; i++)
    {
        for(int j=0 ; j<=m ; j++)
        {
            for(int k=0 ; k<=j ; k++)
            {
                if(dp[i][j]<dp[i-1][k]+mo[i][j-k])
                {
                    dp[i][j]=dp[i-1][k]+mo[i][j-k];
                    for(int h=1 ; h<i ; h++)
                    pa[i][j][h]=pa[i-1][k][h];
                    pa[i][j][i]=j-k;
                }
            }
        }
    }
    printf("%d\n",dp[n][m]);
    for(int i=1 ; i<=n ; i++)
        printf("%d %d\n",i,pa[n][m][i]);
    return 0 ;
}

这里给的数据很小,如果数据大的话,有个空间优化

#include <iostream>

using namespace std;

int f[20],n,m,w[20][20],ans[20][20];

int main()
{
    int i,j,k;

    cin>>n>>m;

    for (i=1;i<=n;++i)
    {
        for (j=1;j<=m;++j)
        {
            cin>>w[i][j];
        }
    }

    for (i=n;i>0;--i)
    {
        for (j=m;j>=0;--j)
        {
            for (k=1;k<=j;++k)
            {
                if (f[j-k]+w[i][k]>f[j])
                {
                    f[j]=f[j-k]+w[i][k];
                    ans[i][j]=k;    //保存f(i,j)取最大时k的值
                }
            }
        }
    }

    cout<<f[m];

    for (i=1,j=m;i<=n;++i)
    {
        cout<<endl<<i<<" "<<ans[i][j];
        j-=ans[i][j];   //算出最优方案第i+1~n公司共使用了几台机器,也就是f(i,j)是由f(i+1,?)转移过来的
    }

    return 0;
}

 

转载于:https://www.cnblogs.com/shuaihui520/p/9480274.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值