打卡 day 6
昨天写小白月赛,今天又在写大创的申报书,鸽了一天。
先写个题面:n个民众选m个组成陪审团,每个民众对于控方和辩方都有一个喜好值,选的这m个民众,对两方的每方喜好值之和的差的绝对值最小,如果有差一样小的,就选和最大的。
其实昨天是把这题开了的,但是没想出来怎么做,看了大佬的代码也不是很懂具体的思想。今天抽时间来好好想想。
第一个点就是 fix ,修正值,就是每一个人的两个喜好值差值最大是20,然后一共m个人,我们从20*m的位置作为起点,这样差值是负值就避免了数组越界,[0,200)我们定为负值,[200,400]我们定为正值。
第二步就是开始传递,三重循环,找出各种人数在各个评价差上的最大总评价之和。m相当于背包的容量,每一次进入一个重量为1的物品,k就是j选择的d-p的差值。(k=d-p)
记录路径就是把每一位选择的民众用vector存储在d-p这个差值的位置。
只有m用完,d-p的最小值才能确定。
第三步就是找,从中间开始找,找到差值最小的位置,记录下标。然后根据 |d-p| 的值计算出d与p的值:
d
=
(
∣
d
−
p
∣
+
(
d
+
p
)
∣
d=(|d-p|+(d+p)|
d=(∣d−p∣+(d+p)∣
p
=
(
(
d
+
p
)
−
∣
d
−
p
∣
)
p=((d+p)-|d-p|)
p=((d+p)−∣d−p∣)
我模了
AC代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=205;
struct node{
int p;
int d;
int add;
int sub;
}a[maxn];
int t;
int dp[25][maxn<<2];
int main(){
int n,m;
while(scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
vector<int> path[25][maxn<<2];
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].p,&a[i].d);
a[i].add=a[i].p+a[i].d;
a[i].sub=a[i].d-a[i].p;
}
memset(dp,-1,sizeof(dp));
int fix=20*m;//修正值
dp[0][fix]=0;
for(int i=1;i<=n;i++){
for(int j=m-1;j>=0;j--){
for(int k=0;k<=2*fix;k++){
if(dp[j][k]>=0){
if(dp[j][k]+a[i].add>dp[j+1][k+a[i].sub]){
dp[j+1][k+a[i].sub]=dp[j][k]+a[i].add;
path[j+1][k+a[i].sub]=path[j][k];
path[j+1][k+a[i].sub].push_back(i);
}
}
}
}
}
int ans;
for(int i=0;i<=fix;i++){
if(dp[m][fix-i]>=0||dp[m][fix+i]>=0){
ans=i;
break;
}
}
int res=dp[m][fix-ans]>=dp[m][fix+ans]?fix-ans:fix+ans;
int d=(res-fix+dp[m][res])/2;
int p=(dp[m][res]-res+fix)/2;
int len=path[m][res].size();
printf("Jury #%d\n",++t);
printf("Best jury has value %d for prosecution and value %d for defence: \n",p,d);
for(int i=0;i<len;i++){
printf("%d ",path[m][res][i]);
}
printf("\n");
}
}