题目1
机器分配
题目描述
某总公司拥有高效生产设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为总公司提供一定的盈利。问:如何分配这M台设备才能使公司得到的盈利最大?求出最大盈利值。
分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。其中M<=100,N<=100。
输入格式
第一行为两个整数M,N。接下来是一个N×M的矩阵,其中矩阵的第i行的第j列的数Aij表明第i个公司分配j台机器的盈利。所有数据之间用一个空格分隔。
输出格式
只有一个数据,为总公司分配这M台设备所获得的最大盈利。
样例数据
input
3 2
1 2 3
2 3 4
output
4
状态:
dp[i][j]——前i 个公司分j 台设备的最大盈利。
状态转移方程:
如果前i − 1 个公司已经分配完了,考虑第i 个公司分配的机器数量,设分得k 台,
k 可能为i ~j ,那么前i − 1 个公司只能分配j − k台。
设a [ x ] [ y ] 表示第x个公司分配y台设备的盈利。
因此,只需要考虑第i ii个公司分配了多少台机器:
f[i][j]=max(f[i][j],f[i-1][j-k]+a[i][k])
边界:
dp[i][0]=0,i 个公司分配0 台设备盈利为0
dp[0][j]=0,0 个公司分配j 台设备盈利为0
输出方案
最后的答案为dp[n][m],即n 个公司分m 台设备的最优解。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[110][110],f[110][110];
int main()
{
freopen("allot.in","r",stdin);
freopen("allot.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
}
f[i][0]=0;
}
for(int i=1;i<=m;i++)
{
for(int j=0;j<=n;j++)
{
f[i][j]=f[i-1][0];
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];
}
}
}
}
cout<<f[m][n];
return 0;
}
补充:
此类问题有的时候爆空间,可以用滚动数组来写;
本题可以根据相邻两层对2取余结果不同来完成数组的滚动;
#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[110][110];
int f[3][110];
void init()
{
cin>>m>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
}
void c_dp()
{
for(int i=1;i<=m;i++)
{
f[1][i]=a[1][i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int pp=0;
for(int k=0;k<=j;k++)
{
pp=max(f[(i-1)%2][k]+a[i][j-k],pp);
}
f[i%2][j]=pp;
}
}
cout<<f[n%2][m];
}
int main()
{
freopen("allot.in","r",stdin);
freopen("allot.out","w",stdout);
init();
c_dp();
}
题目2
两棵苹果树
题目描述
很少有人知道奶牛爱吃苹果。农夫约翰的农场上有两棵苹果树(编号为1和2),每一棵树上都长满了苹果。奶牛贝茜无法摘下树上的苹果,所以她只能等待苹果从树上落下。但是,由于苹果掉到地上会摔烂贝茜必须在半空中接住苹果(没有人爱吃摔烂的苹果)。贝茜吃东西很快,所以她接到苹果后仅用几秒钟就能吃完。
每一分钟,两棵苹果树其中的一棵会掉落一个苹果。贝茜已经过了足够的训练,只要站在树下就一定能接住这棵树上掉落的苹果。同时,贝茜能够在两棵树之间快速移动(移动时间远少于1分钟),因此当苹果掉落时她必定站在两棵树其中的一棵下面。此外,奶牛不愿意不停地往返于两棵树之间,因此会错过一些苹果。
苹果每分钟掉落一个,共T(1<=T<=1000)分钟,贝茜最多愿意移动W(1<=W<=100)次。现给出每分钟掉落苹果的树的编号,要求判定贝茜能够接住的最多苹果数。
开始时贝茜在1号树下。
输入格式
第1行:由空格隔开的两个整数:T和W。
第2..T+1行:1或2(每分钟掉落苹果的树的编号)。
输出格式
一行:在贝茜移动次数不超过W的前提下她能接到的最多苹果数
样例数据
input
7 2
2
1
1
2
2
1
1
output
6
用d[i][j]表示前i分钟移动j次接的苹果数
状态转移方程:
j为偶数: dp[i][j]=max(dp[i-1][j]+flag[i][2],dp[i-1][j-1]+flag[i][2]);
j为奇数: dp[i][j]=max(dp[i-1][j]+flag[i][1],dp[i-1][j-1]+flag[i][1]);
#include<bits/stdc++.h>
using namespace std;
int flag[1100][3];
int dp[1010][110];
int m,n;
int main()
{
freopen("apple.in","r",stdin);
freopen("apple.out","w",stdout);
cin>>m>>n;
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
flag[i][x]=1;
}
for(int i=1;i<=m;i++)
{
dp[i][0]=dp[i-1][0]+flag[i][1];
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(j&1)
dp[i][j]=max(dp[i-1][j]+flag[i][2],dp[i-1][j-1]+flag[i][2]);
else
dp[i][j]=max(dp[i-1][j]+flag[i][1],dp[i-1][j-1]+flag[i][1]);
}
}
int ans=-1;
for(int i=0;i<=n;i++)
{
ans=max(ans,dp[m][i]);
}
cout<<ans<<endl;
return 0;
}
题目3
马棚
题目描述
每天,小明和他的马外出,然后他们一边跑一边玩耍。当他们结束的时候,必须带所有的马返回马棚,小明有K个马棚。
他把他的马排成一排然后跟随它走向马棚,因为他们非常疲劳,小明不想让他的马做过多的移动。
因此他想了一个办法:将马按照顺序放在马棚中,后面的马放的马棚的序号不会小于前面的马放的马棚的序号。
而且,他不想他的K个马棚中任何一个空置,也不想任何一匹马在外面。已知共有黑、白两种马,而且它们相处得并不十分融洽。
如果有i个白马和j个黑马在一个马棚中,那么这个马棚的不愉快系数将是i∗j。所有k个马棚不愉快系数的和就是系数总和。
确定一种方法把n匹马放入k个马棚,使得系数总和最小。
输入格式
在第一行有两个数字:n(1≤n≤500)和k(1≤k≤n)。
在接下来的n行是n个数。在这些行中的第i行代表队列中的第i匹马的颜色:1意味着马是黑色的,0意味着马是白色的。
输出格式
只输出一个单一的数字,代表系数总和可能达到的最小值。
样例数据
input
6 3
1
1
0
1
0
1
{6匹马,3个马棚}
{第1匹马为黑马}
{第3匹马为白马}
output
2
对于前i个马棚,装前j匹马的不愉快系数,
可以考虑对于前i-1个马棚,装前k匹马,
然后剩下的一个马棚装l+1到j匹马。
枚举分界点k,在所有可能的情况中取得一个最小值即可。
dp[i][j]=min(dp[i][j],dp[i−1][l]+f[l+1][j])
记得预处理一下黑马白马的不愉快系数;
#include<bits/stdc++.h>
using namespace std;
int dp[555][555],f[555][555];
int a[555],b[555],w[555];
int n,k;
int main()
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=b[i-1];
w[i]=w[i-1];
if(a[i]==1)
{
b[i]++;
}
else w[i]++;
}
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
f[i][j]=(b[j]-b[i])*(w[j]-w[i]);
}
}
memset(dp,10,sizeof(dp));
for(int i=1;i<=n;i++)
{
dp[1][i]=b[i]*w[i];
}
for(int i=1;i<=k;i++)
{
for(int j=i;j<=n-k+i;j++)
{
for(int l=i-1;l<j;l++)
{
dp[i][j]=min(dp[i][j],dp[i-1][l]+f[l][j]);
}
}
}
cout<<dp[k][n];
return 0;
}
题目4
时间安排
题目描述
小王参加的考试是几门科目的试卷放在一起考,一共给t分钟来做。他现在已经知道每门科目花的时间和得到的分数的关系,还有写名字要的时间(他写自己的名字很慢)请帮他算一下他最高能得几分。
总分一定时,第一门科目成绩尽量高,第一门科目成绩也一样时,第二门科目成绩尽量高…………以次类推。如果放弃某一门的考试(花的时间为0),那么名字也就不用写了。
输入格式
第一行三个正整数t,n,name。 t是总时间,n表示考n个科目,name表示写名字要的时间(每一门科目写名字时间一样)。
接下来n行,每行t个正整数,第i个数表示时间为i时这门科目的分数(不一定递增)。时间为0时这门科目的分数为0,所以就不读入了。
输出格式
一个数,即总分。
样例数据
input
5 3 1
1 2 3 4 5
2 3 3 4 6
3 3 3 2 5
output
6
f[i][j]数组表示前i门花j分钟可得到的最高分
转移方程
f[i][j]=max(f[i][j],f[i-1][j-k]+a[i][k-name]);
此题与工程分配类似,多了一个name花费时间,
因此在比较的时候还要减掉写名字花的时间:
即a[i][k-name]
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,t,name;
int a[110][110],f[110][110];
int main()
{
freopen("score.in","r",stdin);
freopen("score.out","w",stdout);
cin>>t>>n>>name;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=t;j++)
{
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=t;j++)
{
f[i][j]=f[i-1][j];
for(int k=name;k<=j;k++)
{
f[i][j]=max(f[i][j],f[i-1][j-k]+a[i][k-name]);
}
}
}
cout<<f[n][t];
return 0;
}
题目5
复制书稿
题目描述
现在要把m本有顺序的书分给k个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。
现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
输入格式
第一行两个整数m,k;(k≤m≤500)
第二行m个整数,第i个整数表示第i本书的页数
输出格式
共k行,每行两个整数,第i行表示第i个人抄写的书的起始编号和终止编号。
k行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。
样例数据
input
9 3
1 2 3 4 5 6 7 8 9
output
1 5
6 7
8 9
最短时间为抄写页数最多的时间,所以要先求最多的抄写页数,从中选择最小值
1. 设状态:f[i][j]//将前i本书分给j个人抄写需要的最短时间;
2. 初始状态:f[i][1];//一个人抄写前i本书需要的最短时间就是前i本书的页数
最终状态:f[m][k];//将前m本书分给j个人抄写需要的最短时
3. 状态转移方程:先分配最后一个人抄写的页数,求得最大值 。
max(f[i-l][j-1],d[i]-d[i-l]);
最后求最短时间
f[i][j]=min(max(f[i-l][j-1],d[i]-d[i-l]))}
求具体方案:
在用动态规划求得最优值后,然后用贪心的思想,将最后一本书按逆序将书分配给k个人抄写,从第k个人开始,如果他还能写,就给他,直到分配完毕。
#include<bits/stdc++.h>
using namespace std;
int i,j,x,y,m,n,k,t,l;
int a[501];//存储每本书的页数
int f[501][501];//f[i][j]表示前i本书分给j个人抄写的最短复制时间
int d[501];//d[j]表示前j本书的总页数
int print(int i,int j)
{
int t,x;
if(j==0) return 0;
if(j==1)
{
cout<<1<<" "<<i<<endl;
return 0;
}
t=i;
x=a[i];
while(x+a[t-1]<=f[m][k])
{
x=x+a[t-1];
t--;
}
print(t-1,j-1);
cout<<t<<" "<<i<<endl;
}
int main()
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
cin>>m>>k;
for(i=0;i<=500;i++)
{
for(j=0;j<=500;j++)
{
f[i][j]=10000000;//对f[i][j]进行初始化
}
}
for(i=1;i<=m;i++)
{
cin>>a[i];
d[i]=d[i-1]+a[i];
f[i][1]=d[i];//把前i本书都分给1个人抄写需要的最短时间
}
for(j=2;j<=k;j++)//j个人
{
for(i=1;i<=m;i++)//i本书
{
for(l=1;l<=i-1;l++)//最后一个人抄写的页数
{
if(max(f[i-l][j-1],d[i]-d[i-l])<f[i][j])
f[i][j]=max(f[i-l][j-1],d[i]-d[i-l]);
}
}
}
print(m,k);
}
题目6
花店橱窗
题目描述
假设你想以最美观的方式布置花店的橱窗。现在你有F束不同品种的花束,同时你也有至少同样数量的花瓶被按顺序摆成一行。这些花瓶的位置固定于架子上,并从1至V顺序编号,V是花瓶的数目,从左至右排列,则最左边的是花瓶1,最右边的是花瓶V。花束可以移动,并且每束花用1至F间的整数唯一标识。标识花束的整数决定了花束在花瓶中的顺序,如果<I<J,则令花束I必须放在花束J左边的花瓶中。
例如,假设一束杜鹃花的标识数为1,一束秋海棠的标识数为2,一束康乃馨的标识数为3,所有的花束在放入花瓶时必须保持其标识数的顺序,即:杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须放在康乃馨左边的花瓶中。如果花瓶的数目大于花束的数目。则多余的花瓶必须空置,且每个花瓶中只能放一束花。
每一个花瓶都具有各自的特点。因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果,并以美学值(一个整数)来表示,空置花瓶的美学值为零。
在上述例子中,花瓶与花束的不同搭配所具有的美学值,如下表所示
| 花瓶编号 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 杜鹃花 | 7 | 23 | -5 | -24 | 16 |
| 秋海棠 | 5 | 21 | -4 | 10 | 23 |
| 康乃馨 | -21 | 5 | -4 | -20 | 20 |
例如,根据上表,杜鹃花放在花瓶2中,会显得非常好看;但若放在花瓶4中则显得十分难看。
为取得最佳美学效果,你必须在保持花束顺序的前提下,使花束的摆放取得最大的美学值。如果有不止一种的摆放方式具有最大的美学值,你只要输出字典序最小的那种摆放方式。
输入格式
第一行包含两个数:F,V。
随后的F行中,每行包含V个整数,Aij 即为输入文件中第(i+1)行中的第j个数。
1≤F≤100,其中F为花束的数量,花束编号从1至F。
F≤V≤100,其中V是花瓶的数量。
−50≤Aij≤50,其中Aij是花束i在花瓶j中的美学值。
输出格式
一行是程序所产生摆放方式的美学值。
第二行必须用f个数表示摆放方式,即该行的第k个数表示花束k所在的花瓶的编号。
样例数据
input
3 5
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20
output
53
2 4 5
用a[i][j]表示美学值,b[i][j]表示前i束花放入j个花瓶中的最优解,
那么通过动态规划依次求出1-i束花在1-j个花瓶中的最优解,
最后输出m[f][v]即可
首先对于第一束花,最优值是和美学值相等的,
所以我们直接赋值a[1][j]=b[1][j]。
在仔细审题发现题目有一个天然的限制:第j朵花最多只能放在第j个瓶子中,
于是我们就可以枚举它放在可以放到的每一个瓶子(从i到j)里所产生的情况下
产生的子问题从而计算在前i个瓶子中放j朵花的最大每美学值;
即m[i-1][k]为第i-1束花在第k个花瓶里的最优解。
将k从i到j-1进行循环,所以状态转移方程:
即b[i][j]=max(b[i-1][k]+a[i][j],b[i][j]);
这道题还要求输出过程,只需要开一个ans数组记录在dp[i][j]时的选择就行了,
到了最后在用栈结构反推过来输出;(确实可以,这真牛)
#include<bits/stdc++.h>
using namespace std;
int f,v,dp[101][101],ma[101][101],ans[101][101];
int main()
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
cin>>f>>v;
for(int i=1;i<=f;i++)
{
for(int j=1;j<=v;j++)
cin>>ma[i][j];
}
for(int i=1;i<=f;i++)
dp[i][i]=dp[i-1][i-1]+ma[i][i];
for(int i=1;i<=v;i++)
for(int j=1;j<=f;j++)
{
dp[i][j]=-1e+8;//初值,因为有负数所以0不行
for(int k=0;k<=i-j;k++)//枚举子问题
{
if(dp[k+j-1][j-1]+ma[j][k+j]>dp[i][j])
{
ans[i][j]=k+j;
dp[i][j]=dp[k+j-1][j-1]+ma[j][k+j];
}
}
}
cout<<dp[v][f]<<endl;//输出最大值
stack<int> st;
int a=f,b=v;
for(int i=1;i<=f;i++)//反推过程
{
st.push(ans[b][a]);
b=ans[b][a]-1;
a--;
}
bool f=1;
while(!st.empty())//输出过程
{
if(f)
{
f=0;
cout<<st.top();
}
else cout<<" "<<st.top();
st.pop();
}
}
当然也可以不用栈,我是比较懵的,同学大佬的写法,
#include<bits/stdc++.h>
using namespace std;
int F,v;
int a[110][110];
int f[110][110];
int g[110][110];
int ans[110];
void init()
{
cin>>F>>v;
for(int i=1;i<=F;i++)
for(int j=1;j<=v;j++) scanf("%d",&a[i][j]);
}
void work()
{
for(int i=0;i<=F+5;i++)
for(int j=0;j<=v+5;j++) f[i][j]=-1e9;
for(int i=1;i<=v-F+1;i++) f[1][i]=a[1][i];
for(int i=2;i<=F;i++)
for(int j=i;j<=v-(F-i);j++)
for(int k=i-1;k<j;k++)
if(f[i-1][k]+a[i][j]>f[i][j])
{
f[i][j]=f[i-1][k]+a[i][j];
g[i][j]=k;
}
}
void print()
{
int j;
int ans1=-1e9;
for(int i=F;i<=v;i++)
{
if(f[F][i]>ans1)
{
ans1=f[F][i];
j=i;
}
}
cout<<ans1<<endl;
ans[F]=j;
for(int i=F-1;i>=1;i--)
{
ans[i]=g[i+1][j];
j=g[i+1][j];
}
for(int i=1;i<F;i++) cout<<ans[i]<<" ";
cout<<ans[F]<<endl;
}
int main()
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
init();
work();
print();
return 0;
}
题目7
雇用计划
【问题描述】
一位管理项目的经理想要确定每个月需要的工人,他当然知道每月所需的最少工人数。当他雇佣或解雇一个工人时,会有一些额外支出。一旦一个工人被雇佣,即使他不工作,他也将得到工资。这位经理知道雇佣一个工人的费用,解雇一个工人的费用和一个工人的工资。现他在考虑一个问题:为了把项目的费用控制在最低,他将每月雇佣或解雇多少个工人。
【输入格式】
输入文件含有三行。第一行为月数n(不超过12)。第二行含雇佣一个工人的费用,一个工人的工资和解雇一个工人的费用(≤100)。第三行含n个数,分别表示每月最少需要的工人数(≤1000)。每个数据之间有一个空格隔开。
【输出格式】
输出仅一行,表示项目的最小总费用。
【样例】
employ.in
3
4 5 6
10 9 11
employ.out
199
该3个月份中最大工人数目为11,最小工人数目为9
第一个月我们可以雇佣10个工人,或者11个工人
第二个月我们需要的工人底线是9个,如果该月雇佣9个
(需要解雇x人,x由上一个月雇佣人数决定(上一个月人数减去9),
如果该月雇佣10个人(需要解雇人数x,也由上一个月雇佣人数决定),
如果该月雇佣11个人,那么现在我们可能还需要继续的雇佣工人,
第三个月也依次类推,
这样不同的雇佣策略导致我们的花费肯定不同,在这些方案在花费中找到最小的即可
dp的关键在于明白:前面一个月雇佣多少个人使得这个月的花费最小
dp[i][j] :代表在第i个月的时候,雇佣J个人的总费用(雇佣费+工资)
dp[i][j]=
dp[i-1][k](前面一个月雇佣k个人,可以使得前面的i个月(包括i)的花费最小)+这个月的费用
=dp[i-1][k]+abs(j-k)*v+j*工资 v表示解雇或者雇佣的权值,由j,k相对大小决定(分类)
#include<bits/stdc++.h>
using namespace std;
int n;
int a,b,c;
int aa[20];
int f[15][1010];
int m;
int main()
{
freopen("employ.in","r",stdin);
freopen("employ.out","w",stdout);
cin>>n;
cin>>a>>b>>c;
for(int i=1;i<=n;i++)
{
scanf("%d",&aa[i]);
m=max(m,aa[i]);
}
memset(f,10,sizeof(f));
for(int i=1;i<=m;i++)
{
f[1][i]=(a+b)*i;
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=aa[i-1];k<=m;k++)
{
if(k<j)
{
f[i][j]=min(f[i][j],f[i-1][k]+a*(j-k)+b*j);
}
else
{
if(k==j) f[i][j]=min(f[i][j],f[i-1][k]+b*j);
else f[i][j]=min(f[i][j],f[i-1][k]+c*(k-j)+b*j);
}
}
}
}
int ans=1e9;
for(int i=aa[n];i<=m;i++)
{
ans=min(f[n][i],ans);
}
cout<<ans<<endl;
return 0;
}
心累。。
本文总结了几道关于资源分配的动态规划题目,包括机器分配、苹果树问题、马棚问题、时间安排、复制书稿和花店橱窗等。通过对每个问题的描述、输入输出格式和状态转移方程的解析,展示了如何使用动态规划解决这类问题。

被折叠的 条评论
为什么被折叠?



