题目
有n(n<=1e3)个糖果,Petra和Jan轮流取
第i个糖果有pi,ji两个权值,谁取了就会得到其对应的权值
Petra会选pi最大的,相同情况下会选ji最小的
Jan会选让自己最后总和最大的,相同情况下会选让Petra最后总和最大的
题目会告诉谁先手,问按两人策略取最后的两人价值和
思路来源
https://blog.csdn.net/keshuai19940722/article/details/20235947
题解
先按p值从大到小排序,p值相同按j值从小到大排序,代表Petra的选择顺序
dp[i][j]代表前i个糖果,Jan从中取走j个的自己收益的最大总和
cost[i][j]代表前i个糖果,Jan从中取走j个的对方收益的最小损失
最大化对方收益,就是最小化对手损失,然后就变成了取不取的背包问题
Petra先手时,先让Petra取一个,就变成了Jan先手;
Petra先手时,Jan能从2-n里取一半,否则能从1-n里取一半;
Jan先手时,Jan能从i个里面取(i+1)/2即一半向上取整
题解的下标,写的非常巧妙
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
struct node
{
int p,j;
}e[N];
bool operator<(node a,node b)
{
return a.p>b.p||(a.p==b.p&&a.j<b.j);
}
int t,n,k;
int sum,dp[N][N],cost[N][N];
char s[10];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
scanf("%s",s);
if(s[0]=='P')k=1;
else k=0;
sum=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&e[i].p,&e[i].j);
sum+=e[i].p;
}
sort(e+1,e+n+1);
memset(dp,0,sizeof dp);
memset(cost,0,sizeof cost);
for(int i=1;i<=n-k;++i)
{
for(int j=1;j<=(i+1)/2;++j)
{
if(dp[i-1][j]>dp[i-1][j-1]+e[i+k].j)
dp[i][j]=dp[i-1][j],cost[i][j]=cost[i-1][j];
else if(dp[i-1][j]==dp[i-1][j-1]+e[i+k].j)
dp[i][j]=dp[i-1][j],cost[i][j]=min(cost[i-1][j],cost[i-1][j-1]+e[i+k].p);
else dp[i][j]=dp[i-1][j-1]+e[i+k].j,cost[i][j]=cost[i-1][j-1]+e[i+k].p;
}
}
printf("%d %d\n",sum-cost[n-k][(n-k+1)/2],dp[n-k][(n-k+1)/2]);
}
return 0;
}