洛谷 p2066 机器分配
这个题是我自己打出来的第二道题~\(≧▽≦)/~啦啦啦,虽然想+改好像得有2小时,并且解法很不像正解。
对于n组数据,我们发现,当前f【m】的一组可能解为w1台+w2台(w1+w2=j),此时我们并不关心w1和w2是怎么来的。
f【m】的全部解为
0,1,2,3....m
m,m-1,m-2......0
也就是说,我们只需要不停找到一个可控的f【1~m】与一组未知的数继续和出一个新的f【1~m】,直到和完全部的组。这可能就是区间dp的雏形吧。
下面来证一下它的可行性:
f【m】的全部解为
0,1,2,3....m 现最优解 q1
m,m-1,m-2......0 未求的 q2
以m-2和2为例,q1中的2为1~求得q1这一组中,所有分配为2的最大值,那么m-2+(q1中的2)>=m-2+其它任意分配为2(1~求得q1这一组中)
b【第几次合并】【坑位(最大可分配数量)】【组数】用来记录第几组分配了几个
共合并n-1次,初始f[i]=a[1][i]
没有被更新的b数组就从上一次中继承,now_i相当于w1是现最优解中的,now_j相当于w2是未知中的。
但是,还有一个坑点:要求答案的字典序最小
需要把第二层循环j和第三层循环k全部改为倒序,因为更改now_i值和now_j值时条件为严格大于,所以非字典序的答案即使相同也不会被记录。
#include <iostream> #include <cstdio> using namespace std; int n,m,a[11][16],f[16],b[16][18][16]; int now_i,now_j; 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]); for(int i=1;i<=m;i++){ f[i]=a[1][i]; b[1][i][1]=i; } for(int i=2;i<=n;i++){ for(int j=m;j>=1;j--){ now_i=-2,now_j=-2; for(int k=j;k>=1;k--){ if(f[j-k]+a[i][k]>f[j]){ now_i=j-k; now_j=k; f[j]=f[j-k]+a[i][k]; } } if(now_i==-2&&now_j==-2){ for(int k=1;k<=i;k++) b[i][j][k]=b[i-1][j][k]; } else{ b[i][j][i]=now_j; for(int k=1;k<i;k++)//不要把i也更新进去 b[i][j][k]=b[i-1][now_i][k]; } } } printf("%d\n",f[m]); for(int i=1;i<=n;i++) printf("%d %d\n",i,b[n][m][i]); return 0; }
感谢上帝~