题目:POJ1015.
题目大意:给定
个人,每个人两种属性
,现在要求选出
个人使得这
个人的
属性之和与
属性之和的差的绝对值最小,在差的绝对值最小的情况下还要求
与
之和最大,并输出
之和与
之和以及选的人的编号.
.
神tm一道背包调了半天…
首先容易想到直接按照差设DP状态,设 表示前 个人中选出了 个人并且差为 是否可行.不过由于还要求 之和最大,所以设 表示前 个人中选出了 个人并且差为 的条件下 之和最大是多少,初始全部设为负无穷, .
容易列出方程:
根据背包的套路可以在优化掉第一维的
,不过这时要倒着枚举
.方程变为:
优化之后考虑如何输出方案,考虑记录一下 表示状态 是通过哪一个 转移过来的.
然后写完之后,交上去直接WA…
怎么WA了呢?可以想一下, 记录的是 通过哪一个 转移过来.可是在输出方案的时候,若当前选了 ,则上一个必定要选编号小于 的,但是 记录的可能有大于等于 的,这个状态混乱了.
容易发现我们并不知道,所以考虑把 开成一个vector,每次更新的时候就把这个东西加进去,最后输出方案的时候每次多记一下当前状态是哪一个 ,一定要选 中最大的小于 的状态,暴力枚举实现即可.
DP时间复杂度 ,输出方案时间复杂度 ,总时间复杂度 .
代码如下:
#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;
}