您的位置:首页 > 其它

洛谷Oj-P1199 三国游戏-博弈

2018-03-21 22:52 197 查看
问题描述:

小涵很喜欢电脑游戏,这些天他正在玩一个叫做《三国》的游戏。

在游戏中,小涵和计算机各执一方,组建各自的军队进行对战。游戏中共有 N 位武将(N为偶数且不小于 4),任意两个武将之间有一个“默契值”,表示若此两位武将作为一对组合作战时,该组合的威力有多大。游戏开始前,所有武将都是自由的(称为自由武将,一旦某个自由武将被选中作为某方军队的一员,那么他就不再是自由武将了),换句话说,所谓的自由武将不属于任何一方。

游戏开始,小涵和计算机要从自由武将中挑选武将组成自己的军队,规则如下:小涵先从自由武将中选出一个加入自己的军队,然后计算机也从自由武将中选出一个加入计算机方的军队。接下来一直按照“小涵→计算机→小涵→……”的顺序选择武将,直到所有的武将被双方均分完。然后,程序自动从双方军队中各挑出一对默契值最高的武将组合代表自己的军队进行二对二比武,拥有更高默契值的一对武将组合获胜,表示两军交战,拥有获胜武将组合的一方获胜。

已知计算机一方选择武将的原则是尽量破坏对手下一步将形成的最强组合,它采取的具体策略如下:任何时刻,轮到计算机挑选时,它会尝试将对手军队中的每个武将与当前每个自由武将进行一一配对,找出所有配对中默契值最高的那对武将组合,并将该组合中的自由武将选入自己的军队。 下面举例说明计算机的选将策略,例如,游戏中一共有 6 个武将,他们相互之间的默契值如下表所示:



双方选将过程如下所示:



小涵想知道,如果计算机在一局游戏中始终坚持上面这个策略,那么自己有没有可能必胜?如果有,在所有可能的胜利结局中,自己那对用于比武的武将组合的默契值最大是多少? 假设整个游戏过程中,对战双方任何时候均能看到自由武将队中的武将和对方军队的武将。为了简化问题,保证对于不同的武将组合,其默契值均不相同。

博弈搜索代码(严重超时):

int n;
int a[510][510];//默契值
bool book[510];//标记数组
vector<int> v;
int max_han = -inf;
bool win = false;
bool check()
{
bool mark[510];
memset(mark,0,sizeof(mark));//初始化标记数组
int sz = v.size();
for(int i = 0; i <= sz - 1; ++i)
mark[v[i]] = true;//被标记的是属于小涵的武将

int max_h = -inf;
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(mark[i] == true && mark[j] == true)//小涵的武将中
max_h = max(max_h,a[i][j]);//默契值的最大值

int max_c = -inf;
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(mark[i] == false && mark[j] == false)//计算机的武将中
max_c = max(max_c,a[i][j]);//默契值的最大值

max_han = max(max_han,max_h);//取默契值的最大值的最大值
if(max_h > max_c)//判断胜负
return true;
else
return false;
}
void dfs(int step,int who)//搜索的层数,当前选择方是谁
{
if(step == n + 1)//边界条件
{
if(check() == true)//如果小涵赢了
win = true;//则存在解
return;
}
if(who == 1)//如果是小涵
{
for(int i = 1; i <= n; ++i)//对于每一个武将
{
if(book[i] == false)//如果是自由的
{
book[i] = true;//标记
v.push_back(i);//加入数组
dfs(step + 1,-who);
v.erase(v.end() - 1);//取消对武将的选择
book[i] = false;//取消标记
}
}
}
else//如果是计算机,按照计算机的策略来
{
int sz = v.size();
int max_val = -inf;//初始化
int k;//记录下标
for(int i = 0; i <= sz - 1; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(a[v[i]][j] > max_val && book[j] == false)
{
max_val = a[v[i]][j];//找出小涵下一步将形成的最强组合
k = j;
}
}
}
book[k] = true;//破坏
dfs(step + 1,-who);
book[k] = false;// !!!,一定不要忘记恢复
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n - 1; ++i)//输入默契值
{
for(int j = i + 1; j <= n; ++j)
{
int t;
cin >> t;
a[i][j] = t;
a[j][i] = t;
}
}
dfs(1,1);//搜索
if(win == true)
{
cout << 1 << endl;
cout << max_han << endl;
}
else
cout << 0 << endl;
return 0;
}


AC代码:

int n;
int a[510][510];
vector<int> v;
int main()
{
cin >> n;
//输入默契值
for(int i = 1; i <= n - 1; ++i)
{
for(int j = i + 1; j <= n; ++j)
{
int t;
cin >> t;
a[i][j] = t;
a[j][i] = t;
}
}
for(int i = 1; i <= n; ++i)
{
int Max = -inf;//最大
int SecMax = -inf;//次大
for(int j = 1; j <= n; ++j)//注意在选出最大的循环结束后才能选次大
Max = max(Max,a[i][j]);
for(int j = 1; j <= n; ++j)//不能放到一个循环中同时进行
if(a[i][j] < Max)//小于最大值的默契值中的最大值
SecMax = max(SecMax,a[i][j]);
v.push_back(SecMax);//加入容器
}
sort(v.begin(),v.end(),greater<int>());//降序排序
cout << 1 << endl << v[0] << endl;//输出最大值
return 0;
}


解决方法:

搜索妥妥地超时,不过分人物角色的搜索我倒是第一次写

小涵是必胜的,因为计算机永远受制于人,是被动的

对于每一个武将x,都有与其默契值最大的另一位武将y

无论是小涵还是计算机,必不能将x和y收入囊中,这是由计算机破坏最大默契值的策略决定的

如果1号武将与其他武将的默契值都很高(2除外),2号武将与其他武将的默契值都很低,那么当小涵选择了武将1,计算机进行破坏(选的不是武将2),那么当小涵继续选择武将2,计算机不会去破坏武将2中的最大默契值

为了简化分析,我们先只进行两轮游戏

武将编号为1,……,n,我们用(i,j)来表示武将i与武将j的默契值,用max[i]来表示武将i默契值中的最大值,sec[i]来表示武将i默契值中的次大值

第一轮游戏:

小涵选择武将x,则计算机选择武将y。此时,(x,y) = max[x],计算机成功
c1e0
进行了破坏

第二轮游戏:

小涵可以选择武将z((x,z) = sec[x]),这也是他能拿到的最大默契值

这时候,计算机有2种可能的选将方法(最大的默契值可能产生于x,也可能产生于z)

①如果最大的默契值产生于x,则计算机选择武将i,有(x,i) > max[z],且(x,i)为此时武将x能达到的最大默契值

②如果最大的默契值产生于z,则计算机选择武将j,(z,j) = max[z]

对于①,我们知道:与武将x默契值最大(被破坏)次大(在小涵手中)已被选走,此时计算机破坏的是第三大,完全不会造成伤害

对于②,小涵仍可以选择sec[z]来应对

不断重复下去,所有的最大值都被计算机破坏,小涵的手中掌握了若干次大值

最大的次大值就是小涵武将中最大的默契值。

只要掌握了必胜策略,就可以在两局内定胜负
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: