@购物@
@题目描述@
小T和小L都很喜欢购物,但是,众所周知,小T和小L都很懒,因此,在这么热的天,他们宁可在淘宝上买东西。现在,货架上有N件物品,由于小T和小L共用一台电脑,他们决定轮流购买自己喜欢的东西。由于小T和小L的审美完全不同,因此,每件物品对小T的价值为Ai,对小L的价值为Bi。因为小L和小T是好朋友,因此他们在满足自己利益的时候也会为别人着想。小T总是会选择对自己价值最大的物品,如果有多个,则选择其中对小L价值最小的。而小L就聪明很多(因为小L会算 != =||),他总是选择会使自己最后得到的物品总价值最多的物品,如果有多个,他会选择使得小L最后得到总价值也最多的物品。
现在,要求聪明的你帮助小L和小T计算按照他们的策略最后分别得到的物品的价值。
输入
第一行一个数N表示物品数。 N ≤ 100
第二行为“L”或者“T”,表示L或者T先选取物品。
第三到第N+2行,每行两个数Ai和Bi
输出
一行用空格隔开两个数P,Q分别表示小T和小L最后得到物品的总价值。
样例输入
4
T
100 80
70 80
50 80
30 50
样例输出
170 130
@分析@
【还是解题之前先吐槽……好一对损友啊……神TM为别人着想然后对别人价值最小=_=】
【这道题一开始样例是错的……导致数据跟着错……怪不得一开始没人AC这道题】
【不过还是用一些奇怪的魔法把数据改对啦( ̄▽ ̄)/】
【显然小L是知道题解的……不然他哪儿来的最优策略……我们去找小L询(kao)问(da)一下吧qwq】
【最后……@TL 这道题的两个人是不是你的幻像qwq】
咳咳扯回正题。
因为小L太神了所以我们先考虑处理小T。按照小T的优先级对物品进行从小到大的排序,则小T的规则变为“从后往前取,尽量取靠后的物品”
如果动态考虑小L每一轮的取法,那这道题简直神题……因为你无法简洁地表达每一回合的状态,100的数据范围状压是行不通的。所以……这里我们需要做一件事情:将思维静态化(离线化)。
考虑最终局面每件物品只会属于小T或小L。根据先后手情况还可以算出每个人所拥有的物品数量。如果小T先手,则 ta 总是取走最后一个物品,局面变为剩下前1~N-1个物品并且小L先手,因此,我们接下来只考虑小L先手的情况。
我们不妨逆向思维:找到合法的最终局面,再使这个最终局面价值对小L最大。这里有个小性质:一个最终局面合法仅当所有后缀中小L拥有物品数量小于等于(后缀长度/2)向上取整。手推一下应该不难证明这个性质……因为小T的行动规则……就那样……非常简单……
因此,我们定义状态
dp[i][j]
d
p
[
i
]
[
j
]
表示以i开始的后缀小L拥有物品数为j的最优状态。转移也不是很难,因为对于第i个物品只有小L拿走与小T拿走两种可能性,所以:
dp[i][j]=best(dp[i+1][j]+a[i].T,dp[i+1][j−1]+a[i].L)
d
p
[
i
]
[
j
]
=
b
e
s
t
(
d
p
[
i
+
1
]
[
j
]
+
a
[
i
]
.
T
,
d
p
[
i
+
1
]
[
j
−
1
]
+
a
[
i
]
.
L
)
只要在转移过程中保证j合法( j <= (N-i)/2 + 1 )即可。
不要忘记将小T先手的情况转换为小L先手的情况。
@代码@
一旦想通了关键点……本题应该就不会很难啦……
至少我是这么想的_(:з」∠)_
如果还有什么问题的话,可以参考代码或者留言在下面哦~
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100;
const int INF = (1<<30);
struct node{
int T, L;
node(int t=0, int l=0):T(t), L(l){}
}a[MAXN + 5], dp[MAXN + 5][MAXN + 5], ans;
bool operator < (node a, node b) {
if( a.T == b.T ) return a.L > b.L;
else return a.T < b.T;
}
char s[2]; int N;
int main() {
scanf("%d%s", &N, s);
for(int i=1;i<=N;i++)
scanf("%d%d", &a[i].T, &a[i].L);
sort(a+1, a+N+1);
if( s[0] == 'T' ) {
ans.T = a[N].T;
N--;
}
for(int i=N;i>=1;i--) {
for(int j=0;j<=(N-i)/2+1;j++) {
dp[i][j] = dp[i+1][j];
dp[i][j].T += a[i].T;
}
for(int j=1;j<=(N-i)/2+1;j++) {
if( dp[i+1][j-1].L + a[i].L > dp[i][j].L ) {
dp[i][j] = dp[i+1][j-1];
dp[i][j].L += a[i].L;
}
else if( dp[i+1][j-1].L + a[i].L == dp[i][j].L && dp[i+1][j-1].T > dp[i][j].T ) {
dp[i][j] = dp[i+1][j-1];
dp[i][j].L += a[i].L;
}
}
}
ans.L += dp[1][(N+1)/2].L;
ans.T += dp[1][(N+1)/2].T;
printf("%d %d\n", ans.T, ans.L);
}
【顺便,这道题好像只有在我们学校内部OJ才能找到……不知道教练怎么弄来的pwp】
@END!@
就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~