蓝书(算法竞赛进阶指南)刷题记录——POJ1015 Jury Compromise(DP)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/90677555

题目:POJ1015.
题目大意:给定 n n 个人,每个人两种属性 a i , b i a_i,b_i ,现在要求选出 m m 个人使得这 m m 个人的 a a 属性之和与 b b 属性之和的差的绝对值最小,在差的绝对值最小的情况下还要求 a a b b 之和最大,并输出 a a 之和与 b b 之和以及选的人的编号.
1 n 2001 , m 20 , 0 a i , b i 20 1\leq n\leq 2001,\leq m\leq 20,0\leq a_i,b_i\leq 20 .

神tm一道背包调了半天…

首先容易想到直接按照差设DP状态,设 f [ i ] [ j ] [ k ] f[i][j][k] 表示前 i i 个人中选出了 j j 个人并且差为 k k 是否可行.不过由于还要求 a , b a,b 之和最大,所以设 f [ i ] [ j ] [ k ] f[i][j][k] 表示前 i i 个人中选出了 j j 个人并且差为 k k 的条件下 a , b a,b 之和最大是多少,初始全部设为负无穷, f [ 0 ] [ 0 ] [ 0 ] = 0 f[0][0][0]=0 .

容易列出方程:
f [ i ] [ j ] [ k ] = max { f [ i 1 ] [ j ] [ k ] , f [ i 1 ] [ j 1 ] [ k a i + b i ] + a i + b i } f[i][j][k]=\max\{f[i-1][j][k],f[i-1][j-1][k-a_i+b_i]+a_i+b_i\}

根据背包的套路可以在优化掉第一维的 i i ,不过这时要倒着枚举 j j .方程变为:
f [ j ] [ k ] = max { f [ j ] [ k ] , f [ j 1 ] [ k a i + b i ] + a i + b i } f[j][k]=\max\{f[j][k],f[j-1][k-a_i+b_i]+a_i+b_i\}

优化之后考虑如何输出方案,考虑记录一下 h i [ j ] [ k ] hi[j][k] 表示状态 f [ j ] [ k ] f[j][k] 是通过哪一个 i i 转移过来的.

然后写完之后,交上去直接WA…

怎么WA了呢?可以想一下, h i [ j ] [ k ] hi[j][k] 记录的是 f [ j ] [ k ] f[j][k] 通过哪一个 i i 转移过来.可是在输出方案的时候,若当前选了 i i ,则上一个必定要选编号小于 i i 的,但是 h i hi 记录的可能有大于等于 i i 的,这个状态混乱了.

容易发现我们并不知道,所以考虑把 h i [ j ] [ k ] hi[j][k] 开成一个vector,每次更新的时候就把这个东西加进去,最后输出方案的时候每次多记一下当前状态是哪一个 i i ,一定要选 h i hi 中最大的小于 i i 的状态,暴力枚举实现即可.

DP时间复杂度 O ( n m 2 max { a i , b i } ) O(nm^2\max\{a_i,b_i\}) ,输出方案时间复杂度 O ( n m ) O(nm) ,总时间复杂度 O ( n m 2 max { a i , b i } ) O(nm^2\max\{a_i,b_i\}) .

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=200,M=20,O=M*M,INF=(1<<30)-1;

int n,m,a[N+9],b[N+9];
int dp[M+9][O*2+9],o;
vector<int>hi[M+9][O*2+9];
int ad,ap,ans[M+9],ca;

void Dfs_plan(int j,int k,int fi){
  if (!j) return;
  int siz=hi[j][k].size(),i=0;
  for (;i<siz&&hi[j][k][i]<fi;++i);
  i=hi[j][k][i-1];
  Dfs_plan(j-1,k-a[i]+b[i],i);
  ans[++ca]=i;
  ad+=a[i];ap+=b[i];
}

Abigail into(){
  for (int i=1;i<=n;++i)
    scanf("%d%d",&a[i],&b[i]);
}

Abigail work(){
  o=m*M;
  for (int i=0;i<=m;++i)
    for (int j=0;j<=o<<1;++j){
      dp[i][j]=-INF;
      hi[i][j].clear();
	}
  dp[0][o]=0;
  for (int i=1;i<=n;++i)
    for (int j=m;j>=1;--j)
      for (int k=-o;k<=o;++k){
	    int hk=k-a[i]+b[i];
	    if (hk<-o||hk>o) continue;
		int hv=dp[j-1][o+hk];
		if (hv==-INF) continue;
	    hv+=a[i]+b[i];
	    if (hv<=dp[j][o+k]) continue;
	    dp[j][o+k]=hv;hi[j][o+k].push_back(i);
	  }
  int t=0;
  for (;dp[m][o+t]==-INF&&dp[m][o-t]==-INF;++t);
  if (dp[m][o+t]<dp[m][o-t]) t=-t;
  ad=ap=ca=0;Dfs_plan(m,o+t,n+1);
}

Abigail outo(int cas){
  printf("Jury #%d\n",cas);
  printf("Best jury has value %d for prosecution and value %d for defence: \n",ad,ap);
  for (int i=1;i<=ca;++i)
    printf(" %d",ans[i]);
  puts("\n");
}

int main(){
  int cas=0;
  while (~scanf("%d%d",&n,&m)&&n+m){
    into();
    work();
    outo(++cas);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/90677555