CERC-2014 K题 - The Imp (博弈DP)

来源:互联网 发布:stm32f 输入端口写法 编辑:程序博客网 时间:2024/06/11 19:16

今天打了一场CERC 2014的题目,确实感觉不一样,有点lrj书上的题目的感觉,灵活、看似简单实则难解、算法隐藏较深。  场上只出了4题,都是水题。

赛后发现很多DP,该题就是一道博弈DP。   怪物想让我得到的最少,我想得到的多,两者都选择最优策略,是不是很眼熟? 其实方法很像紫书P279 ”最大面积最小的三角剖分“ 。

像背包问题,我们按照一定的阶段来递推,那么对于当前物品i ,我又两种决策:取还是不取 。而对于怪物,也有两种决策:用魔法还是不用(前提是还能发动),上面说了,两者都用最优策略,所以状态方程就很显然了:if(j>0)  d[i][j] = max(d[i+1][j],min(a[i].v-a[i].c,d[i+1][j-1]-a[i].c)); 

问题是提交后WA,实在想不出bug,搜了唯一一篇题解上说:通过反证法可以发现,最优序列一定是价值递增的。  

后来经过高神的讲解,大致明白了为什么要先买价值低的。因为怪物不知道未买商品的价值,而我自己只能获得一件商品的价值,那么肯定要将价值高的保留到后面买。

对于相邻两件物品,到底先买哪一件,我们可以做一个假设,每一个假设都有两种情况:价格的高低分类。 这样,我们可以得出结论,其实无论什么情况都应该先买价值低的(请自己试着证明),当然,这是在怪物一定释放魔法的情况下 。而怪物要是不放魔法呢?  其实在DP的过程中已经选取最优方案了(分成了怪物是否释放魔法和我是否买一件商品),所以我可以向后买价值高的。  这样,就完整的解释了为什么要事先按照价值进行排序了。

细节参见代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<vector>#include<map>#include<list>#include<cmath>#include<set>#include<queue>using namespace std;typedef long long ll;const int INF = 100000000;const int maxn = 200000;int T,n,m,kase = 0,k,d[maxn][20],vis[maxn][20];struct node{    int v,c;    bool operator < (const node& rhs) const {        return v < rhs.v;    }}a[maxn];int dp(int i,int j) {    int& ans = d[i][j];    if(i == n+1) return ans = 0;    if(vis[i][j] == kase) return ans;    vis[i][j] = kase;    ans = -INF;    int v;    if(j > 0) v = min(a[i].v-a[i].c,dp(i+1,j-1)-a[i].c);    else if(j == 0) v = a[i].v-a[i].c;    ans = max(dp(i+1,j),v);    return ans;}int main() {    scanf("%d",&T);    while(T--) {        scanf("%d%d",&n,&k);        for(int i=1;i<=n;i++) {            scanf("%d%d",&a[i].v,&a[i].c);        }        ++kase;        sort(a+1,a+n+1);        int ans = dp(1,k);        printf("%d\n",ans);    }    return 0;}

0 0
原创粉丝点击