根本不知道怎么下手,后面看了别人的博客:
POJ 1085 The Triangle War
* 题目注意:这道题中对于线段是谁放置的不关心,只要自己放置的线段形成一个完整的三角形就是获得了一次奖励
* 题目分析:
* 这道题目是关于博弈的搜索题,这里使用动态规划的思想来做,而动态规划的关键是状态的表示和存储。
* 由于游戏的局面状态变化总是和线段是否放置相关,于是可以这样来表达和记录局面状态,用二进制形式表示
* 线段,2^i表示第i条线段(i从0开始),这样的话,局面状态state也可以用线段二进制的或运算结果来表示了,而且
* 满足:0<= state < 2^18 。
* 这里还需要注意的是状态表f[state]中记录的是对于当前局面state,某一方先走,然后双方都采取最优策略,最终双方
* 所拥有的新产生的三角形个数的差值
*/
* 题目注意:这道题中对于线段是谁放置的不关心,只要自己放置的线段形成一个完整的三角形就是获得了一次奖励
* 题目分析:
* 这道题目是关于博弈的搜索题,这里使用动态规划的思想来做,而动态规划的关键是状态的表示和存储。
* 由于游戏的局面状态变化总是和线段是否放置相关,于是可以这样来表达和记录局面状态,用二进制形式表示
* 线段,2^i表示第i条线段(i从0开始),这样的话,局面状态state也可以用线段二进制的或运算结果来表示了,而且
* 满足:0<= state < 2^18 。
* 这里还需要注意的是状态表f[state]中记录的是对于当前局面state,某一方先走,然后双方都采取最优策略,最终双方
* 所拥有的新产生的三角形个数的差值
*/
从这道题目中,可以看出动态规划表存储值的定义需要实现明确的。
还可以用启发式搜索,我还不怎么会,后面再来补充;
他的代码:
题目大意是2个人玩一种游戏,每次放1条边,如果生成了三角形,则归他所有
给定一些初始的走法,问都以最优策略走谁最后拿的三角形多?
很早做的,有点难度,详细注释
dp,或者说是博弈
//4448583_AC_485MS_1468K
/**********************************************************************
* Online Judge : POJ
* Problem Title : Triangle War
* ID : 1085
* Date : 12/3/2008
* Time : 8:23:54
* Computer Name : EVERLASTING-PC
***********************************************************************/
/*
线段使用二进制存储,2^i表示第i条线段的hash值
0<=state<2^18,二进制表示当前棋盘状态
f(state) 表示在state这个状态下,A先走,双方走法都是最优,最终A比B多得到的三角形个数
0<=i<18,记place(state,2^i)=t,则
f(state)=max
{
t -f(state|2^i) 当t=0
t +f(state|2^i) 当t>0
}
*/
#include<iostream>
using namespace std;
//储存组成每个三角形的线段hash值
int tri[9][3]=
{
{0x1,0x2,0x4},
{0x4,0x10,0x20},
{0x8,0x10,0x80},
{0x20,0x40,0x100},
{0x200,0x400,0x8000},
{0x80,0x400,0x800},
{0x800,0x1000,0x10000},
{0x100,0x1000,0x2000},
{0x2000,0x4000,0x20000}
};
//存线段端点标号
int line[18][2]=
{
{1, 2},{1, 3},{2, 3},{2, 4},{2, 5},{3, 5},
{3, 6},{4, 5},{5, 6},{4, 7},{4, 8},{5, 8},
{5, 9},{6, 9},{6, 10},{7, 8},{8, 9},{9, 10}
};
//2的幂
int exp2[18]=
{
0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000,0x10000,0x20000
};
/*
全局变量
f[] 将f()的值存下,避免重复计算
state 当前棋盘状态
MIN 初始化的极小值
*/
int f[0x40000];
int state;
const int MIN=-10;
//计算在state这个状态下,放置某线段新得到的三角形数
int place(int state,int hash)
{
int already,t,get=0;
for(int i=0;i<9;++i)
{
already=0;t=3;
for(int j=0;j<3;++j)
{
//放置的线段是第i个三角形中的第j条
if(tri[i][j]&hash)
{
t=j;
}
//第i个三角形中的第j条已经放置
else if(tri[i][j]&state)
{
already++;
}
}
//不存在于第i个三角形中
if(t==3)
{
continue;
}
//则第i个三角形是新得到的
if(already==2)
{
get++;
}
}
return get;
}
//f()
int dp(int state)
{
//如果当前状态已算过就直接用
if(f[state]!=MIN)
{
return f[state];
}
int max=MIN,t;
for(int i=0;i<18;++i)
{
//如果第i条线段没有用过
if(!(state&exp2[i]))
{
//放置第i条线段
t=place(state,exp2[i]);
//递归计算
t+=((t)?1:-1)*dp(state|exp2[i]);
//更新最大值
max=(t>max)?t:max;
}
}
//存下当前状态的最大值,并返回
return (f[state]=max);
}
int main()
{
//freopen("in_1085.txt","r",stdin);
//全局初始化
for(int i=0;i<0x40000;++i)
{
f[i]=MIN;
}
f[0x3FFFF]=0;
/*
b 数据块数
n 每块的局数
m 每局已经走的步数
x,y 读入的线段
t 临时变量
player 表示当前谁走(1 A,-1 B)
pre 根据给出的步骤A比B多得到的三角形数
*/
int b,n,m,x,y,t,player,pre;
//cin>>b;
//for(int i=0;i<b;++i)
{
cin>>n;
for(int j=0;j<n;++j)
{
//每局初始化
pre=0;
state=0;
player=1;
cin>>m;
for(int k=0;k<m;++k)
{
//读入线段
cin>>x>>y;
for(int l=0;l<18;++l)
{
//转化为hash并放置
if(line[l][0]==x&&line[l][1]==y)
{
t=place(state,exp2[l]);
state|=exp2[l];
break;
}
}
//新得到三角形
if(t)
{
pre+=(player*t);
}
//没有生成新三角形则换另一方走
else
{
player*=-1;
}
}
//计算状态并输出,>0即A胜
cout<<"Game "<<j+1<<": "<<((pre+player*dp(state)>0)?'A':'B')<<" wins./n";
}
cout<<endl;
}
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn<a href="http://lib.csdn.net/base/dotnet" class='replace_word' title=".NET知识库" target='_blank' style='color:#df3434; font-weight:bold;'>.NET</a>/twilightgod/archive/2008/12/12/3505659.aspx
我的代码:(精简一些)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
const int MAXN = 1<<18;
//三角形的hash值
const int triangle[9][3] = {
{1, 2, 4}, {8, 16, 128}, {4, 16, 32},
{32, 64, 1024}, {256, 512, 32768}, {128, 512, 2048},
{2048, 4096, 65536}, {1024, 4096, 8192}, {8192, 16384, 131072}
};
//别人的这个数组,学到了,嘿嘿嘿(1, 2, 4, 8 , 10)
int tri[9][3]=
{
{0x1,0x2,0x4},
{0x4,0x10,0x20},
{0x8,0x10,0x80},
{0x20,0x40,0x100},
{0x200,0x400,0x8000},
{0x80,0x400,0x800},
{0x800,0x1000,0x10000},
{0x100,0x1000,0x2000},
{0x2000,0x4000,0x20000}
};
//存线段端点标号
const int line[18][2] = {
{1, 2}, {1, 3}, {2, 3}, {2, 4}, {2, 5}, {3, 5},
{3, 6}, {4, 5}, {4, 7}, {4, 8}, {5, 6}, {5, 8},
{5, 9}, {6, 9}, {6, 10}, {7, 8}, {8, 9}, {9, 10}
};
//2的幂
const int haha[18] = {
1, 2, 4, 8, 16, 32, 64, 128, 256,
512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072
};
int dp[MAXN];
//状态为i时,放置2的幂为ii的那条线,形成的三角形的个数;
int place(int i, int ii) {
int tmp = 0;//三角形个数
for(int k = 0; k < 9; k++) {
int nAngle = 0;
for(int t = 0; t < 3; t++) {
if ((triangle[k][t]&i))
nAngle++;//第k个三角形的第t条边是否已经被放置
if (triangle[k][t]&ii)
nAngle += 2;//这条边在第k个三角形里
}
if (nAngle == 4)//自行体会
tmp++;
}
return tmp;
}
//用dp坐的话,肯定超时,我还不知道这样写对了没有,多求了很多东西(备忘录搜索的优势,只求了有用的状态,而且写起来简单)
/*
void init() {
for(int i = 0; i < MAXN-64; i++) {
int m = 0;
for(int j = 0; i < 18; j++)
if (i<<j&1) m++;
if (m > 11) continue;
for(int j = 0; j < 18; j++) {
int ii = 1<<j;
if ((ii|i) == i) continue;
int tmp = place(i, ii);
if (tmp)
tmp += dp[i];
else tmp -= dp[i];
ii |= i;
dp[ii] = max(dp[ii], tmp);
}
}
}
*/
//表示在state这个状态下,假设A先走,双方走法都是最优,最终A比B多得到的三角形个数
void dfs(int state) {//备忘录搜索
if (dp[state] != -88) {//-88嘿嘿
return ;
}
for(int i = 0; i < 18; i++)
if (!(haha[i]&state)) {//如果第i条线没有放,
int tmp = place(state, haha[i]);//那就放
dfs(state|haha[i]);//下一状态
dp[state] = max(((tmp)?1:-1)*dp[state|haha[i]]+tmp, dp[state]);//自行体会
}
}
int main() {
int t, m, pp = 1;
for(int i = 0; i < MAXN; i++)
dp[i] = -88;
dp[MAXN-1] = 0;//初始化
scanf("%d", &t);
while (t--) {
scanf("%d", &m);
int x, y, res = 0, now = 1, state = 0;//res,结果,now谁走,state当前状态
//先模拟
while (m--) {
scanf("%d%d", &x, &y);
int tt;
for(int k = 0; k < 18; k++)
if (line[k][0] == x && line[k][1] == y) {
tt = place(state, haha[k]);
state |= haha[k];
break;
}
if (!tt) now *= -1;//换人不是 = ,是 *=
res += now*tt;
}
dfs(state);
res += now*dp[state];
printf("Game %d: %c wins.\n", pp++, res>0?'A':'B');
}
}