[洛谷P4232]无意识之外的捉迷藏-线性规划-纳什均衡

无意识之外的捉迷藏

题目背景

(五)心与心的对话

旧都还在下着雪。

不知道走了多远的路,已经远离街市了。

眼前隐隐约约能看到一座巨大的宫殿。

那就是,地灵殿吗?

心里突然紧张了起来。

这里住着旧地狱最可怕的觉妖怪。

古明地觉,她拥有读心的能力。人类,妖怪,甚至是怨灵,站在她面前的时候都如同赤身裸体,没有任何秘密可言。而且,听说在战斗中,她还会使用催眠术不断激起对手内心深处的恐怖回忆,从心灵上打垮对手。这样的妖怪自然会被人讨厌啦。

不过,此次地底之旅可不能因为要遇到可怕的妖怪而就此结束。

咚咚咚,敲了敲地灵殿的大门。

“来客吗,真是少见呢。”

眼前,站着一个少女,穿着蓝色的衣服,粉红色的裙子,头发也是粉红色的。

在她的胸前,悬着一只红色的大眼睛,通过眼睛周围的六根管子连接着身体。

她看起来很温柔的样子,完全不像听说的那样恐怖。

“来地底旅行的外面世界的人类吗?真是非常少见呢,居然找到了这里”

“看起来没有别的想法,就是想来转转呢,那就进来吧”

走进大门。

不愧是地灵”殿”,真的是好大的一个宫殿,桃红色和黑色相间的地板,印有花纹的窗户。

眼前是一组很宽的台阶,通向二楼,然后分成左右两个通道。

“很漂亮吧,这里空间大,宠物们都很喜欢呢。”

就这样,我跟着觉进入了她工作的房间,我们坐在沙发上聊了很久,虽然我很少说话。

在聊天的过程中,我了解到她还有一个妹妹古明地恋,由于不愿让别人因为自己会读心而讨厌自己,闭上了觉之瞳。觉为了开导妹妹,经常和宠物陪着她玩。

地灵殿,觉和妹妹,宠物们,一直在过着平静又温馨的生活。

“既然来这里了,就和我们一起玩吧。”觉邀请我们参与她们的游戏。

地底的妖怪会玩怎样的游戏呢?

于是,就这样,我就答应参与觉和恋的“无意识的捉迷藏”了。

说是”捉迷藏”,其实和普通的捉迷藏区别很大,更类似于”捉人游戏”。

就是觉和恋一开始分别站在两个地方,觉要捉到恋就算赢了。

但为什么又说是”捉迷藏”呢?

原来恋恋可以无意识地行动,也就是可以让周围人在潜意识里忽略她的存在,类似隐身,但又不是隐身。真是有趣的能力呢,是不是闭上了觉之瞳的缘故?

我们玩得很开心。有时无意识碰到了恋恋的手,还吓了一跳呢。

一段时间后,姐妹俩累了,觉还有工作要处理,就先回去了。

宠物们似乎意犹未尽,她们还想继续。

“可是在这个旧地狱啊,除了主人的妹妹恋以外,哪里又有妖怪能够操纵无意识呢?

算了,干脆玩普通的捉人游戏吧。”阿燐提议道。

于是宠物们很快又忘我地投入了”无意识之外的捉迷藏”中。

不知什么时候,我感到背后一凉,回过头一看,原来是恋恋。

我们就这样站在这里看着宠物们玩。

虽然不知道为什么能耐心地看那么长时间,但几个小时过去了,我们依然站在这里。

恋恋好像有一些疑问,在经过简单的交流后,我把她的疑问做了一个总结。

(见题目描述)

这个问题对无意识的恋恋来说果然无法解决啊。

能和姐妹俩聊得这么开心,真是很感激呢,那就尽自己的努力思考一下这个问题吧。

(后续剧情见题解,接下来请看T4)

题目描述

问题摘要:

在一个有向无环图上,阿燐和阿空第0个时刻分别站在编号为 s r s , s k s 的节点,二人都知道双方的初始位置,对地图完全了解。

从第1个时刻起,每个时刻阿燐和阿空都可以选择站着不动,也可以选择移动到相邻的节点,二人每时刻的移动是同时开始的,并且不能中途改变方向。

阿燐被阿空捉住时,游戏立即结束。如果阿空一直没有捉住阿燐,第tt 个时刻结束后两人就不能再继续移动了,游戏将在第 t + 1 个时刻结束。

阿空的目的是尽快捉住阿燐(捉住的定义是与阿燐同一时刻站在同一节点),而阿燐的目的是尽可能更长时间不被阿空捉住。具体而言,若一场游戏进行了 t 0 时刻,阿燐的得分是 t 0 ,阿空的得分是 t 0 ,双方都希望自己得分(或得分的期望值)更高。

我们认为在这个过程中阿燐和阿空随时都能知道对方的位置。两人在第 t 个时刻不能看出第 t + 1 个时刻对方要走到哪里。

恋恋想知道,在双方最优决策的情况下,游戏结束时刻的期望值是多少。

输入输出格式

输入格式:

第一行5个整数 n , m , s r , s k , t ,用空格隔开,下同。

n 表示地图点数, m 表示边数。

接下来 m 行,每行两个整数 a , b ,表示从 a b 有一条单向边(不存在重边)。

输出格式:

一个实数,四舍五入保留3位小数,表示游戏结束时刻的期望值。

你的答案必须和标准答案完全相同才算正确。

输入输出样例

输入样例#1:
3 2 1 2 10
1 3
2 3
输出样例#1:
11.000
输入样例#2:
6 8 2 1 2
1 2
1 3
1 5
2 3
3 5
5 6
6 4
2 4
输出样例#2:
2.333

说明

样例解释:

样例1:阿燐只要一直不动,阿空在前 t 单位时间内就无法抓到阿燐,答案为 t + 1 ,即11.000

样例2:无可奉告

数据范围:

对于30%的数据 n 3 ,捆绑测试

对于100%的数据 n , t 20 ,前40%的数据和后30%的数据分别捆绑测试

提示:

本题主要考察你能否使用正确的方法算出答案,对算法运行耗时要求不高。

by orangebird


ob聚聚的神题orz
推了1h+才研究出线性规划模型……


思路:
首先,图是个DAG,那么显然不存在绕圈走的操作。

于是可以发现,对于每一个状态,其期望值依赖于所有子状态的期望值,而子状态不会依赖父亲的期望值。

考虑记忆化搜索,记 f [ i ] [ j ] [ k ] 表示当前阿燐在 i ,阿空在 j ,游戏已经进行了 k 单位时间时,游戏的期望值。

此时,假设当前局面如图:

orz ob聚聚

(ob聚聚题解里的图)

那么可以列出一个收益矩阵,每格对应了双方的一种可能决策,如下:

阿燐\阿空 不动 走到2 走到3 走到5
不动 f[2][1][t+1]+1 1 f[2][3][t+1]+1 f[2][5][t+1]+1
走到3 f[3][1][t+1]+1 f[3][2][t+1]+1 1 f[3][5][t+1]+1
走到4 f[4][1][t+1]+1 f[4][2][t+1]+1 f[4][3][t+1]+1 f[4][5][t+1]+1

显然这个矩阵中任意元素都可以递归求出。

由于双方均使用最优决策,也就是说,任何一方改变策略都会使其收益降低。
所以这就是一个纳什均衡,而目标就是找到这个最优决策点。

考虑如何建出可以使用线性规划的方程。
为了方便,设 e i j 代表收益矩阵的 i j 列。

同样为了方便,从阿空的角度考虑。
根据纳什均衡,阿空的目标是在阿燐对局面的期望值进行最优操作后,最小化局面的期望值。
也就是说,假如将阿空选择每种方案的概率当做自变量,每一种阿燐的方案当做一个函数,那么对于一组阿空的方案,阿燐将会取所有函数得到的值的最大值,而阿空所需要做的就是找到使这些函数的最大函数值的最小的一个点。

那么假设阿空可选择的方案数为 n ,阿燐可选择的方案数为 m ,答案为 V
同时设阿空选择第 i 种方案的概率为 p i ,那么可以得到如下的一个线性规划:

最小化 V

满足

i = 1 n p i = 1

j [ 1 , m ] ,   i = 1 n p i e j i V

考虑设 x i = p i V ,那么 V = 1 x i

于是可以得到一个新的线性规划:

最大化 i = 1 n x i

满足

j [ 1 , m ] ,   i = 1 n x i e j i 1

可以发现,这就是咱们常用的标准型线性规划。

于是直接求解即可~

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

typedef double db;
const int N=29;
const int M=29;
const db eps=1e-8;
const db Inf=19260817;

db a[M][M];
namespace simplex
{
    int n,m;
    int id[N+M];

    inline void pivot(int x,int y)
    {
        swap(id[y],id[n+x]);
        db t=a[x][y];
        a[x][y]=1.0;

        for(int i=0;i<=n;i++)
            a[x][i]/=t;
        for(int i=0;i<=m;i++)
            if(i!=x)
            {
                t=a[i][y];
                a[i][y]=0;
                for(int j=0;j<=n;j++)
                    a[i][j]-=t*a[x][j];
            }
    }

    inline bool init()
    {
        while(1)
        {
            db k=-eps;
            int l=0,e=0;

            for(int i=1;i<=m;i++)
                if(a[i][0]<k)
                    k=a[i][0],l=i;
            if(!l)return 1;
            for(int i=1;i<=n && !e;i++)
                if(a[l][i]<-eps)
                    e=i;
            if(!e)return 0;
            pivot(l,e);
        }
    }

    inline bool simplex()
    {
        while(1)
        {
            db k=1e18;
            int l=0,e=0;

            for(int i=1;i<=n && !e;i++)
                if(a[0][i]>eps)
                    e=i;
            if(!e)return 1;
            for(int i=1;i<=m;i++)
                if(a[i][e]>eps && a[i][0]/a[i][e]<k)
                    k=a[i][0]/a[i][e],l=i;
            if(!l)return 0;
            pivot(l,e);
        }
    }

    inline db mina()
    {
        if(!init())exit(0);
        if(!simplex())exit(0);
        return -a[0][0];
    }

    inline void init(int x,int y)
    {
        n=x;m=y;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                a[i][j]=0.0;
        for(int i=1;i<=n;i++)
            id[i]=i;
        for(int i=1;i<=m;i++)
            id[n+i]=0;
    }
}

int n,m,sr,sk,t;
vector<int> g[N];
db f[N][N][N];
int deg[N];

inline void add(int u,int v)
{
    g[u].push_back(v);
    deg[u]++;
}

inline void dfs(int x,int y,int tim)
{
    if(tim==t+1 || x==y){f[x][y][tim]=0;return;}
    if(f[x][y][tim]!=-Inf)return;

    for(int i=0;i<g[x].size();i++)
        for(int j=0;j<g[y].size();j++)
            dfs(g[x][i],g[y][j],tim+1);

    simplex::init(deg[y],deg[x]);
    for(int i=0;i<g[x].size();i++)
    {
        for(int j=0;j<g[y].size();j++)
            a[i+1][j+1]=f[g[x][i]][g[y][j]][tim+1]+1.0;
        a[i+1][0]=1;
    }

    a[0][0]=0;
    for(int i=1;i<=deg[y];i++)
        a[0][i]=1;

    f[x][y][tim]=1.0/simplex::mina();
}

int main()
{
    n=read();m=read();
    sr=read();sk=read();
    t=read();
    for(int i=1,u;i<=m;i++)
        u=read(),add(u,read());
    for(int i=1;i<=n;i++)
        add(i,i);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=0;k<=t;k++)
                f[i][j][k]=-Inf;

    dfs(sr,sk,0);
    printf("%.3lf\n",f[sr][sk][0]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zlttttt/article/details/79775012