洛谷 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;
}

 感谢上帝~

 

posted @ 2019-06-25 19:04  sdzmq  阅读(179)  评论(0编辑  收藏  举报