蒟蒻吃药计划-治疗系列 #round 1 机器分配+挖地雷
1.机器分配
题目背景
无
题目描述
总公司拥有高效设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。
输入输出格式
输入格式:
第一行有两个数,第一个数是分公司数N,第二个数是设备台数M。
接下来是一个N*M的矩阵,表明了第 I个公司分配 J台机器的盈利。
输出格式:
第1行为最大盈利值
第2到第n为第i分公司分x台
P.S.要求答案的字典序最小
输入输出样例
70 1 1 2 1 3 1
Luogu标签:背包;搜索;动态规划/动规/DP
分析:
我们先设有一二维数组ve并用ve[i][j]表示第 i 个公司分配 j 台机器可获得的盈利
然后开始寻找状态转移方程——
我们先按照公司的顺序分阶段来分配
第 1 个阶段,把M台机器全部分配给第 1 个公司,并记录下盈利的值
第 2 个阶段,把M台机器分给前 2 个公司,记录盈利值并与第 1 阶段的盈利值作比较,记录更优的盈利值
第 3 个阶段,把M台机器分给前 3 个公司,同样地记录下盈利值与之前记录好的最优盈利值作比较,重新记录
······
第 N 个阶段,把M台机器分给所有 N 个公司,记录盈利值,与之前的最优盈利值作比较,得出最后的答案
咳咳,上面的玩意你可能看懂了,但是不容易用代码实现,所以我们把问题抽象,换一个更加直观的表述方式——
我们定义有一二维数组dp,并令dp[i][j]表示前 i 个公司分配 j 台机器可以获得的最大盈利
那么我们从中可知dp[i-1][k]也就是代表前 i-1 个公司分配 k 台机器可以获得的最大盈利
上面应该非常简单易懂吧?
好,那我们进一步深入
我们已经找到了求出最大盈利的方法,那么由方法转移的方程的如下:
dp[i-1][0]+ve[i][j] 表示前 i-1 个公司分配 0 台机器获得的最大盈利+第 i 个公司分配 j 台机器获得的盈利
同理,我们可得
dp[i-1][1]+ve[i][j-1] 表示前 i-1 个公司分配 1 台机器获得的最大盈利+第 i 个公司分配 j-1 台机器获得的盈利
dp[i-1][2]+ve[i][j-2] 表示前 i-1 个公司分配 2 台机器获得的最大盈利+第 i 个公司分配 j-2 台机器获得的盈利
dp[i-1][j-1]+ve[i][1] 表示前 i-1 个公司分配 j-1 台机器获得的最大盈利+第 i 个公司分配 1 台机器获得的盈利
dp[i-1][j]+ve[i][0] 表示前 i-1 个公司分配 j 台机器获得的最大盈利+第 i 个公司分配 0 台机器获得的盈利
因为ve这个二维数组里面的值是不变的,如果我们要是前面每一个式子的值最大,那么阶段 i 的状态就只能从阶段 i-1 中得到,与ve和其他的东西无关
讲了这么多,我们可以通过上面的一系列关系推出状态转移方程:
dp[i][j]=max{dp[i-1][k]+ve[i][j-k]}
代码如下:
1 #include <bits/stdc++.h> 2 #define fp(i,l,r) for(register int i=(l);i<=(r);++i) 3 #define fd(i,l,r) for(register int i=(l);i>=(r);--i) 4 using namespace std; 5 int maxn=0; 6 int dp[32][32],ve[32][32]; 7 int print(int a,int b){ 8 int t; 9 if(a==0){ 10 return 0; 11 } 12 fp(t,0,b){ 13 if(maxn==dp[a-1][t]+ve[a][b-t]){ 14 maxn=dp[a-1][t]; 15 print(a-1,t); 16 printf("%d %d\n",a,b-t); 17 break; 18 } 19 } 20 } 21 int main(){ 22 int n,m; 23 scanf("%d%d",&n,&m); 24 fp(i,1,n){ 25 fp(j,1,m){ 26 scanf("%d",&ve[i][j]); 27 } 28 } 29 fp(i,1,n) { 30 fp(j,1,m){ 31 maxn=0; 32 fp(k,0,j){ 33 if(dp[i-1][k]+ve[i][j-k]>maxn){ 34 maxn=dp[i-1][k]+ve[i][j-k]; 35 } 36 dp[i][j]=maxn; 37 } 38 } 39 } 40 printf("%d\n",dp[n][m]); 41 print(n,m); 42 return 0; 43 }
2.挖地雷
题目背景
NOIp1996提高组第三题
题目描述
在一个地图上有N个地窖(N<=20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入输出格式
输入格式:
输入文件mine.in有若干行。
第1行只有一个数字,表示地窖的个数N。
第2行有N个数,分别表示每个地窖中的地雷个数。
第3行至第N+1行表示地窖之间的连接情况:
第3行有n-1个数(0或1),表示第一个地窖至第2个、第3个、…、第n个地窖有否路径连接。如第3行为1 1 0 0 0 … 0,则表示第1个地窖至第2个地窖有路径,至第3个地窖有路径,至第4个地窖、第5个、…、第n个地窖没有路径。
第4行有n-2个数,表示第二个地窖至第3个、第4个、…、第n个地窖有否路径连接。
… …
第n+1行有1个数,表示第n-1个地窖至第n个地窖有否路径连接。(为0表示没有路径,为1表示有路径)。
输出格式:
输出文件wdl.out有两行数据。
第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。
第二行只有一个数,表示能挖到的最多地雷数。
输入输出样例
1 3 4 5 27
分析:
好,让我来讲一讲这道题目,
题中输入明确写到了是单向图且没有回路,
符合DAG的要求所以这题我们用动态规划的DAG一类问题的解法来完成(废话)
不过……
:救命!我被绑架了,就在能挖到最多地雷的那条路线的终点那个地窖!
啊,看样子我们必须快一点了,可是我们应该怎么办呢
:我分析了整个地窖的通道图,现在全部告诉你 balabalabala……如果你还有什么不懂的,就回到上面去看看!快点啊QAQ
mmp这个家伙真是烦人,不过他给我们提供了地窖的通道图,我们来分析一下:
根据题意,我们知道一共有 N 个地窖,每个地窖里分别有对应数量的地雷,之后的 N-1 行是表示地窖之间的连接情况
作为一名 oier ,还是一名即将救人命的 oier ,我们先把这些东西都输入进去。
输入完了吗?快点!
接下来我们来分析怎样找到挖到最多地雷的路线
我们定义一个 boomer 数组表示每个地窖的地雷数
getter 数组表示以 getter 数组下标作为地窖的编号,从这个地窖开始往后能够挖到的最多的地雷
bester 数组表示以当前下标结尾的编号所对应的地窖能挖出最大地雷的数量
roader 二维数组表示地窖之间的连通,邻接矩阵
之后我们弄出一个双重循环
枚举 i=n-1...1 j=i+1...n
判断 i j 两个地窖是否连通
用一个变量储存 getter[j]
再用一个变量储存上一个变量
再开循环统计就好啦!
很明显,我们可以得出救人的秘钥:
max{boomer[i],getter[j]} (i<j<=n , roader[i][j]=1)
边界条件:
getter[n]=boomer[n]
代码:
1 #include <bits/stdc++.h> 2 #define fp(i,l,r) for(register int i=(l);i<=(r);++i) 3 #define fd(i,l,r) for(register int i=(l);i>=(r);--i) 4 using namespace std; 5 const int INF=0xfffffff; 6 int main(){ 7 int n; 8 int getter[20+5]; 9 int boomer[20+5]; 10 int bester[20+5]; 11 int roader[20+5][20+5]; 12 memset(roader,0,sizeof(roader)); 13 memset(getter,0,sizeof(roader)); 14 memset(bester,0,sizeof(roader)); 15 scanf("%d",&n); 16 fp(i,1,n){ 17 scanf("%d",&boomer[i]); 18 } 19 int sssssssssss; 20 fp(i,1,n-1){ 21 fp(j,i+1,n){ 22 scanf("%d",&roader[i][j]); 23 } 24 } 25 getter[n]=boomer[n]; 26 int l=0,k=0; 27 fd(i,n-1,1){ 28 l=0; 29 k=0; 30 fp(j,i+1,n){ 31 if(roader[i][j]==1&&getter[j]>l){ 32 l=getter[j]; 33 k=j; 34 } 35 } 36 getter[i]=l+boomer[i]; 37 bester[i]=k; 38 } 39 k=1; 40 int maxn=0; 41 fp(i,2,n){ 42 if(getter[i]>getter[k]){ 43 k=i; 44 } 45 } 46 maxn=getter[k]; 47 printf("%d",k); 48 k=bester[k]; 49 while(k!=0){ 50 printf(" %d",k); 51 k=bester[k]; 52 } 53 printf("\n%d",maxn); 54 return 0; 55 }