【CQBZOJ - 2413】购物

该博客分析了一个名为"CQBZOJ - 2413"的购物问题,其中小T和小L轮流选择价值不同的物品。小T总是选择对自己价值最大且对小L价值最小的物品,而小L则追求自己的总价值最大化。博客内容包括题目描述、解题思路和代码实现,重点讨论了解决这个问题的动态规划方法,涉及逆向思维和状态转移方程。

@购物@


@题目描述@

小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][j1]+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!@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值