1 2 2 9 8 7 6
16
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=0,M=0,Z=1e9+7,ms63=0x3f3f3f3f; int casenum,casei; int n,m,top; int a[5][5]; int res[5][5]; int dy[4]={-1,0,0,1}; int dx[4]={0,-1,1,0}; int b[32]; inline bool ok(int y,int x) { return y>=0&&y<n&&x>=0&&x<m; } map<int,int>mop; inline int dfs(int sta,int sum) { if(mop.find(sta)!=mop.end())return mop[sta]; int maxv=0; for(int i=0;i<n;++i) { for(int j=0;j<m;++j)if(res[i][j]<=2) { int o=i*m+j; if(sta&b[o]) { for(int k=0;k<4;++k) { int yy=i+dy[k]; int xx=j+dx[k]; if(ok(yy,xx))--res[yy][xx]; } int tmp=sum-dfs(sta^b[o],sum-a[i][j]); gmax(maxv,tmp); for(int k=0;k<4;++k) { int yy=i+dy[k]; int xx=j+dx[k]; if(ok(yy,xx))++res[yy][xx]; } } } } mop[sta]=maxv; return maxv; } int main() { for(int i=0;i<=30;++i)b[i]=1<<i; scanf("%d",&casenum); for(casei=1;casei<=casenum;++casei) { mop.clear(); int sum=0; MS(res,0); scanf("%d%d",&n,&m); for(int i=0;i<n;++i) { for(int j=0;j<m;++j) { scanf("%d",&a[i][j]); sum+=a[i][j]; for(int k=0;k<4;++k) { int yy=i+dy[k]; int xx=j+dx[k]; if(ok(yy,xx))++res[i][j]; } } } mop[0]=0; printf("%d\n",dfs(b[n*m]-1,sum)); } return 0; } /* 【trick&&吐槽】 这题原来这么水= = 明明可以AC的,看到过的人少就不敢做了,是病得治啊>_< 我竟然放弃了这样题面的一道题,去做计算几何!简直——作!大!死! (话说这题面为什么是这个 我我我我也不知道!) ps:没有做的另外一个原因是,我竟然又读错题了= 。 = 【题意】 给你一个n*m(2<=n,m<=5)的棋盘。 每个方格都有一个棋子,权值为v[][](0<=v[][]<=1000)两个人轮流取数。 一个棋子可以被选的条件是,这个棋子上下左右4个方向中,至少有2个棋子为空或者被取过。 两个人都希望自己的权值尽可能大,都采取最优策略。 让你输出,首先最大可以获得的权值是多少。 【类型】 记忆化搜索 【分析】 这题很类似于旅行商问题。 我们发现—— 棋子数量做多也不过只有25,状态数最多只有2^25,状态数确定,对应权值就确定,和顺序无关,可以很轻松表示。 同时,因为我们取数有一定的限制条件,所以合法的状态数更少。 虽然状态数的总数没那么多,然而状态数的表示,最大还是需要用到2^25,用int存不下,于是考虑用map。 ============================================================================= 工具都准备好了,开始做这道题啦。 不妨定义状态—— mop[sta]表示现在还没有拿的棋子编号的二进制之和记做sta下,先手取数能拿到的最大权值。 同时用辅助变量sum,记录当前所有未取之数权值之和。 显然, 对于初始条件,我们有—— mop[0]=0; 对于转移,我们有—— mop[sta]=max(sum-mop[nxtsta]) 于是,取数限制用res[][]表示,同时套一个记忆化搜索的外壳,这道题就做完了>_<。 然而,现实中的WKC真是太蠢了 T_____T! 【时间复杂度&&优化】 O(合法状态数*log(合法状态数)*25) 这道题一个小优化,就是,我们把当前能选取的位置, 通过一个数组存起来。这样就可以把*25的这个常数优化得小一点了~ */