首页 > 代码库 > hdu 5165 Funny Game (博弈)

hdu 5165 Funny Game (博弈)

hdu 5165 Funny Game (博弈)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5165


题意:
Bob有一个数组{a1,a2,...,an},数组中的每个元素都是介于1到n之间的整数。Bob还有m个函数,他们的定义域和值域都是集合{1,2,...,n}。Bob和Alice轮流开始玩游戏,Alice先开始。对于每一轮,玩家可以选择一个函数f使得数组中每个元素 ai(1 <= i <= n)变成f(ai)。例如,一开始数组是{1,1,2,4,5},有一个函数f(1)=1,f(2)=3,f(3)=4,f(4)=1,f(5)=2。那么经过一次操作,数组变为{1,1,3,1,2}。如果数组中的所有元素都相同(无论当前论是Bob还是Alice),那么Alice胜利游戏结束。然后Bob的目的是阻止Alice胜利。 假设Alice和Bob都足够聪明,每次都采取最优策略。问:无论数组的初始状态是什么,Alice是否都能够必胜?


输入描述:

输入第一行包含一个整数T(1 <= T <= 200)表示测试数据组数。对于每组测试数据:第一行包含两个整数n和m(1 <= n,m <= 100)表示数组的大小和函数的个数。接下来m行,每行包含n个整数f(1),f(2),...,f(n) (1 <= f(i) <= n,1 <= i <= n)。


输出描述:

对于每组数据,如果Alice一定能够必胜,输出YES,否则输出NO。


思路:


1.对于Alice,如果对于每个数对(x,y),都能通过某些函数把它变成(z,z),则Alice必胜,因为由于函数不能一对多,所以这个过程是不可逆的,所以当Alice把(x,y)变成(z,z),Bob不能把它复原,或者变成不一样的数。


2.设(x,y)通过某些函数转换为(z,z)为过程1。
由必胜态倒推,可以看出如果x==y,过程1总是可以成功的。
设dp[x][y][0/1]为对于数对(x,y),Alice先手0,后手1,过程1成功则dp[x][y][0/1]=1,否则dp[x][y][0/1]=0。
所以,在最初的时候dp[i][i][0/1]==1。
我们把dp[x][y][0/1]==1的全部入队,每次取最前面的一个,进行更新,更新分成两种情况:


a)如果Alice此时为先手,则它的前驱为Bob先手。
设Bob先手为事件A,要使事件A的过程1可以成功,则事件A的所有后继的过程1都是成功的。


b)如果Alice此时为后手,则它的前驱为Alice先手。
设Alice先手为事件B,要使事件B的过程1可以成功,则事件B的一个后继的过程1可以成功就行了。


所以一开始建图的时候要建一个正向的图,一个方向的图。


 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*hdu 5165
  题意:
  Bob有一个数组{a1,a2,...,an},数组中的每个元素都是介于1到n之间的整数。Bob还有m个函数,他们的定义域和值域都是集合{1,2,...,n}。Bob和Alice轮流开始玩游戏,Alice先开始。对于每一轮,玩家可以选择一个函数f使得数组中每个元素 ai(1 <= i <= n)变成f(ai)。例如,一开始数组是{1,1,2,4,5},有一个函数f(1)=1,f(2)=3,f(3)=4,f(4)=1,f(5)=2。那么经过一次操作,数组变为{1,1,3,1,2}。如果数组中的所有元素都相同(无论当前论是Bob还是Alice),那么Alice胜利游戏结束。然后Bob的目的是阻止Alice胜利。 假设Alice和Bob都足够聪明,每次都采取最优策略。问:无论数组的初始状态是什么,Alice是否都能够必胜?
  输入描述:
  输入第一行包含一个整数T(1 <= T <= 200)表示测试数据组数。对于每组测试数据:第一行包含两个整数n和m(1 <= n,m <= 100)表示数组的大小和函数的个数。接下来m行,每行包含n个整数f(1),f(2),...,f(n) (1 <= f(i) <= n,1 <= i <= n)。
  输出描述:
  对于每组数据,如果Alice一定能够必胜,输出YES,否则输出NO。
  思路:
  
  1.对于Alice,如果对于每个数对(x,y),都能通过某些函数把它变成(z,z),则Alice必胜,因为由于函数不能一对多,所以这个过程是不可逆的,所以当Alice把(x,y)变成(z,z),Bob不能把它复原,或者变成不一样的数。

  2.设(x,y)通过某些函数转换为(z,z)为过程1。
  由必胜态倒推,可以看出如果x==y,过程1总是可以成功的。
  设dp[x][y][0/1]为对于数对(x,y),Alice先手0,后手1,过程1成功则dp[x][y][0/1]=1,否则dp[x][y][0/1]=0。
  所以,在最初的时候dp[i][i][0/1]==1。
  我们把dp[x][y][0/1]==1的全部入队,每次取最前面的一个,进行更新,更新分成两种情况:
  
  a)如果Alice此时为先手,则它的前驱为Bob先手。
  设Bob先手为事件A,要使事件A的过程1可以成功,则事件A的所有后继的过程1都是成功的。
  
  b)如果Alice此时为后手,则它的前驱为Alice先手。
  设Alice先手为事件B,要使事件B的过程1可以成功,则事件B的一个后继的过程1可以成功就行了。

  所以一开始建图的时候要建一个正向的图,一个方向的图。
 */

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#include<cstring>
using namespace std;
#define MP make_pair
#define PB push_back
const int N=10005;
vector<int> mp[N],rv[N];
map< pair<int,int>,int > vis;
int f[N],p2i[N][N];
int dp[N*N][2];
int n,m;
struct Dt{
    int v,turn;
    Dt(){}
    Dt(int _v,int _turn){ v=_v; turn=_turn; }
};
queue<Dt> que;
void init(){
    int id=0;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            p2i[j][i]=p2i[i][j]=id++;
    for(int i=0;i<id;++i){
        mp[i].clear();
        rv[i].clear();
    }
    vis.clear();
    for(int i=0;i<id;++i){
        dp[i][0]=dp[i][1]=0;
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=0;i<m;++i){
            for(int j=1;j<=n;++j)
                scanf("%d",&f[j]);
            for(int j=1;j<=n;++j)
                for(int k=j+1;k<=n;++k){
                    int fr=p2i[j][k];
                    int to=p2i[f[j]][f[k]];
                    if(vis[MP(fr,to)]==0){
                        mp[fr].PB(to);
                        rv[to].PB(fr);
                        vis[MP(fr,to)]=1;
                    }
                }
        }
        for(int i=1;i<=n;++i){
            que.push(Dt(p2i[i][i],0));
            que.push(Dt(p2i[i][i],1));
            dp[p2i[i][i]][0]=dp[p2i[i][i]][1]=1;
        }
        while(!que.empty()){
            Dt now=que.front();
            que.pop();
            if(now.turn==0){
                int to=now.v;
                for(int i=0;i<rv[to].size();++i){
                    int fr=rv[to][i];
                    if(dp[fr][1]==0){
                        int st=1;
                        for(int i=0;i<mp[fr].size();++i)
                            st&=dp[mp[fr][i]][0];
                        dp[fr][1]=st;
                        if(dp[fr][1])
                            que.push(Dt(fr,1));
                    }
                }
            }
            else{
                int to=now.v;
                for(int i=0;i<rv[to].size();++i){
                    int fr=rv[to][i];
                    if(dp[fr][0]==0){
                        dp[fr][0]=1;
                        que.push(Dt(fr,0));
                    }
                }
            }
        }
        int ans=1;
        for(int i=1;i<=n;++i){
            for(int j=i;j<=n;++j){
                ans&=dp[p2i[i][j]][0];
            }
        }
        if(ans) puts("YES");
        else puts("NO");
    }
    return 0;
}

hdu 5165 Funny Game (博弈)