略复杂的dp题。
有n个人,每个人有两个分数di,pi。从中选出m个人,要求|sigma(di)-sigma(pi)|最小,相同时则输出sigma(di)+sigma(pi)最大的情况。
答案完整输出方案。
dp[i][j]表示i个人的组合里,差值为j的情况下,和值的最大值。
计算每一个人的差值subi,和值sumi
则dp[i][j] = max(dp[i-1][j-subk]+sumk) k∈n
注意dp的时候为了能表示负数,要加一个修正值。
#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; const int maxn = +;
const int X = ;
const int INF = 0x3f3f3f3f; int n,m;
int sum[maxn],sub[maxn];
int dp[][*maxn];
int path[][*maxn]; bool check(int i,int j,int k)
{
int tmp ;
while(path[i][X+j])
{
tmp = path[i][X+j];
if(k == tmp)
return false;
j -= sub[tmp];
i--;
}
return true;
} int main()
{
int cas = ;
while(scanf("%d%d",&n,&m) && n)
{
int d,p;
cas++;
for(int i=;i<=n;i++)
{
scanf("%d%d",&p,&d);
sum[i] = p+d;
sub[i] = p-d;
}
memset(dp,-,sizeof dp);
memset(path,,sizeof path); dp[][X+] = ; int ans_sub = +;
for(int i=;i<=m;i++)
{
for(int j=-;j<=;j++)
{
for(int k=;k<=n;k++)if(dp[i-][X+j-sub[k]] != - && check(i-,j-sub[k],k))
{
if(dp[i-][X+j-sub[k]]+sum[k] > dp[i][X+j])
{
dp[i][X+j] = dp[i-][X+j-sub[k]]+sum[k];
path[i][X+j] = k;
//printf("i:%d j:%d k:%d dp:%d\n",i,j,k,dp[i][X+j]);
if(i==m && ( abs(j) < abs(ans_sub) || (abs(j)==abs(ans_sub) && dp[i][X+j] > dp[i][X+ans_sub]) ) )
{
ans_sub = j;
}
}
}
}
} //printf("%d\n",ans_sub);
printf("Jury #%d\n",cas);
printf("Best jury has value %d for prosecution and value %d for defence: \n",(dp[m][X+ans_sub]+ans_sub)/,(dp[m][X+ans_sub]-ans_sub)/); int ans[],cnt=;
for(int i=m;i>=;i--)
{
ans[cnt++] = path[i][X+ans_sub];
ans_sub -= sub[ans[cnt-]];
}
sort(ans,ans+cnt); for(int i=;i<cnt;i++)
{
printf(" %d",ans[i]);
}
printf("\n\n");
}
}
最近dp写的略顺手