这道题涉及的知识点有点多。。。

所以还是比较有意思的。

domino

描述

迈克生日那天收到一张 N*N 的表格(1 ≤ N ≤ 2000),每个格子里有一个非

负整数(整数范围 0~1000),迈克不喜欢数值太大,他手上有 K 块日字形多米

诺骨牌(1 ≤ K ≤ 8),可以完美覆盖两个相邻格子(上下左右均可)**

问迈克把 K 块骨牌全部放在表格上,使得表格可见整数和最小。

输入

第一行两个数 N,K 用空格隔开 下面 N 行 N 列为该初始表格。

输出

被 K 块骨牌挡住之后剩余数字之和。

分数分布

对于 70%数据,K≤5。

样例输入 1

3 1

2 7 6

9 5 1

4 3 8

样例输出 1

31

样例输入 2

4 2

1 2 4 0

4 0 5 4

0 3 5 1

1 0 4 1

样例输出 2

17

解释下题意,在棋盘上放上k个1*2的骨牌,使覆盖到的格子上的值得和最大。

乍一看,貌似可以贪心,枚举所有骨牌,选择前k大的骨牌。(接下来骨牌大小的定义都是它覆盖的格子的和的大小)。但第二个样例却过不到。

原因很简单,当我们选择了一个骨牌时,会导致顶多7个骨牌不能被选择,如图所示:



(这个骨牌自身也算一个)

如果我们选取的前k大个骨牌在这7个骨牌之中,则不合法,所以导致了错误。

所以我们可以将这7个骨牌全都加入在我们考虑选择的骨牌之中。于是我们考虑的骨牌最多便有了\(50\)个\((7*(8-1)+1)\)

考虑在这\(50\)个骨牌中间深搜,选择其中的\(k\)个,并且保证这些骨牌不会相互覆盖即可。

于是我们就将问题转换成了从50个物品中选出K个,使和最大,同时选择一个物品会导致多个物品不能选择的问题。这明显可以使用状压DP暴力解决。

但是直接暴力会超时,时间复杂度为\(2^{50}\),空间复杂度也实在难以接受。

可以先看看我暴力的代码。

//感觉很好理解所以没怎么打注释
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std; #define N 2010 int A[N][N],n,k,P,siz;
bool mark[51][51]; struct node {
LL x,y,sum,dir;//使用node来存储每个骨牌
node() {}; //x,y表示这个骨牌左上的坐标
node(LL X,LL Y,LL D) {
x=X;y=Y;
sum=A[x][y]+A[x+D][y+1-D];//使用dir变量来表示可能的
dir=D; //两种骨牌
}
}; vector<node> L; vector< pair<LL,LL> > getFuck(node F) {//返回一个vector生成
vector<pair<LL,LL> > D; //一个骨牌的两个坐标
D.push_back( make_pair(F.x,F.y));
D.push_back( make_pair(F.x+F.dir,F.y+1-F.dir));
return D;
} bool Fuck(node A,node B) {
vector< pair<LL,LL> > X=getFuck(A);
vector< pair<LL,LL> > Y=getFuck(B);
for(LL i=0;i<X.size();i++)//判断两个骨牌的4个覆盖区域的
for(LL j=0;j<Y.size();j++)//坐标是否重复
if(X[i]==Y[j])
return 1;
return 0;
} bool cmp(node A,node B) {
return A.sum>B.sum;
} int ans,sum;
bool vis[60]; void dfs(int pos,int haveC) {//暴力深搜,枚举选和不选当前骨牌的情况
ans=max(ans,sum);
if(pos==siz) return ;
dfs(pos+1,haveC);
if(haveC==k) return ;
for(int i=0;i<pos;i++) if(vis[i] && mark[pos][i]) return ;
vis[pos]=1;
sum+=L[pos].sum;
vis[pos]=1;
dfs(pos+1,haveC+1);
vis[pos]=0;
sum-=L[pos].sum;
} int main() {//基操
cin>>n>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>A[i][j],P+=A[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
if(i<n) L.push_back(node(i,j,1));
if(j<n) L.push_back(node(i,j,0));
}
sort(L.begin(),L.end(),cmp);
//for(int i=0;i<L.size();i++) cout<<L[i].sum<<' ';
//cout<<endl;
int Sz=L.size();
siz=min(Sz,50);
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)
if(i!=j && Fuck(L[i],L[j]))
mark[i][j]=1;
dfs(0,0);
cout<<P-ans;
return 0;

正解用的是双向DFS,但我剪了下枝,发现用位运算也可以卡过去。

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std; #define N 2010
#define LL long long
LL A[N][N],n,k,P,siz,lol;
LL mark[51]; struct node {
LL x,y,sum,dir;
node() {};
node(LL X,LL Y,LL D) {
x=X;y=Y;
sum=A[x][y]+A[x+D][y+1-D];
dir=D;
}
}; vector<node> L; vector< pair<LL,LL> > getFuck(node F) {
vector<pair<LL,LL> > D;
D.push_back( make_pair(F.x,F.y));
D.push_back( make_pair(F.x+F.dir,F.y+1-F.dir));
return D;
} bool Fuck(node A,node B) {
vector< pair<LL,LL> > X=getFuck(A);
vector< pair<LL,LL> > Y=getFuck(B);
for(LL i=0;i<X.size();i++)
for(LL j=0;j<Y.size();j++)
if(X[i]==Y[j])
return 1;
return 0;
} bool cmp(node A,node B) {
return A.sum>B.sum;
} LL ans,sum; void dfs(LL pos,LL haveC,LL cover) {
ans=max(ans,sum);
if(pos==siz) return ;
if(haveC==k) return ;
dfs(pos+1,haveC,cover);
if(!(cover & (1ll<<pos))) {
sum+=L[pos].sum;
dfs(pos+1,haveC+1,cover|mark[pos]);
sum-=L[pos].sum;
}
} int main() {
cin>>n>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>A[i][j],P+=A[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
if(i<n) L.push_back(node(i,j,1));
if(j<n) L.push_back(node(i,j,0));
}
sort(L.begin(),L.end(),cmp);
lol=L[0].sum;
//for(int i=0;i<L.size();i++) cout<<L[i].sum<<' ';
//cout<<endl;
LL Sz=L.size();
siz=min(Sz,(LL)50);
for(int i=0;i<siz;i++)
for(int j=0;j<siz;j++)
if(i!=j && Fuck(L[i],L[j]))
mark[i]|=(1ll<<j);
dfs(0,0,0);
cout<<P-ans;
return 0;
}

说下我在做这题时的细节吧

1.所有变量尽量都要开long long,因为答案最终有可能爆int

2.第一部分不一定要占一半,开1<<20不会爆空间。

3.位运算要使用\(1ll<<50\).

这里附上双向DFS的代码,有兴趣的同学可以康康,注释写得挺详细的。

(估计也没人看吧hhh)

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std; #define N 2010
#define LL long long LL A[N][N],n,k,P,siz,asiz,bsiz;
LL mark[54];
LL FFuck[1<<21][9],ans; struct node {
LL x,y,sum,dir;
node() {};
node(LL X,LL Y,LL D) {
x=X;y=Y;
sum=A[x][y]+A[x+D][y+1-D];
dir=D;
}
}; vector<node> L; vector< pair<LL,LL> > getFuck(node F) {
vector<pair<LL,LL> > D;
D.push_back( make_pair(F.x,F.y));
D.push_back( make_pair(F.x+F.dir,F.y+1-F.dir));
return D;
} bool Fuck(node A,node B) {
vector< pair<LL,LL> > X=getFuck(A);
vector< pair<LL,LL> > Y=getFuck(B);
for(LL i=0;i<X.size();i++)
for(LL j=0;j<Y.size();j++)
if(X[i]==Y[j])
return 1;
return 0;
} bool cmp(node A,node B) {
return A.sum>B.sum;
} //дµÃÕæ³ó¹þ¹þ¹þ
void dfs1(LL pos,LL haveC,LL state,LL cover,LL sum) {
if(haveC>k) return ;
if(pos==asiz) {
FFuck[state][haveC]=max(FFuck[state][haveC],sum);
return ;
}
dfs1(pos+1,haveC,state,cover,sum);
if(!(cover & (1ll<<pos))) {//
LL mss=L[pos].sum;
dfs1(pos+1,haveC+1,state|(1ll<<pos),cover|mark[pos],sum+mss);
}
} void dfs2(LL pos,LL haveC,LL state,unsigned LL cover,LL sum) {
if(haveC>k) return ;
if(pos==siz) {
ans=max(ans,sum+FFuck[(~cover)&((1ll<<asiz)-1)][k-haveC]);
return ;
}
dfs2(pos+1,haveC,state,cover,sum);
if(!(cover & (1ll<<pos))) {
LL mss=L[pos].sum;
dfs2(pos+1,haveC+1,state|(1ll<<pos),cover|mark[pos],sum+mss);
}
} int main() {
cin>>n>>k;
if(n==1) {cout<<0; return 0;}
LL MM=(k-1)*7+1;
for(LL i=1;i<=n;i++)
for(LL j=1;j<=n;j++)
cin>>A[i][j],P+=A[i][j];
for(LL i=1;i<=n;i++)
for(LL j=1;j<=n;j++) {
if(i<n) L.push_back(node(i,j,1));
if(j<n) L.push_back(node(i,j,0));
}
sort(L.begin(),L.end(),cmp);
//for(LL i=0;i<L.size();i++) cout<<L[i].sum<<' ';
//cout<<endl;
LL Sz=L.size();
siz=min(Sz,MM);
asiz=min(siz/2,(LL)20);
for(LL i=0;i<siz;i++)
for(LL j=0;j<siz;j++)
if(i!=j && Fuck(L[i],L[j]))
mark[i]|=(1ll<<j);
dfs1(0,0,0,0,0);
for(LL c=0;c<=k;c++) {
for(LL i=1;i<(1ll<<asiz);i++) {
LL j=i;
while(j) {
LL t=j&-j;
FFuck[i][c]=max(FFuck[i-t][c],FFuck[i][c]);
j-=t;
}
}
}
dfs2(asiz,0,0,0,0);
cout<<P-ans;
return 0;
}

模拟赛T5 : domino ——深搜+剪枝+位运算优化的更多相关文章

  1. POJ - 3074 Sudoku (搜索)剪枝+位运算优化

    In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For exa ...

  2. Hdu3812-Sea Sky(深搜+剪枝)

    Sea and Sky are the most favorite things of iSea, even when he was a small child.  Suzi once wrote: ...

  3. php实现不用加减乘除号做加法(1、善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍)

    php实现不用加减乘除号做加法(1.善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍) 一.总结 1.善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍 二.ph ...

  4. 数独求解问题(DFS+位运算优化)

    In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For exa ...

  5. N皇后-位运算优化

    N皇后问题 时间限制: 5 Sec  内存限制: 128 MB 题目描述 魔法世界历史上曾经出现过一个伟大的罗马共和时期,出于权力平衡的目的,当时的政治理论家波利比奥斯指出:“事涉每个人的权利,绝不应 ...

  6. N皇后解法以及位运算优化

    N皇后解法以及位运算优化 观察棋盘,要求皇后之间不能处在同行同列同一条斜线,求使得每行都有一个皇后的放置方法共有多少种. 每尝试放置一个皇后,都可以把该位置所在的行.列标号用一个数组标记,含义表示该行 ...

  7. POJ-1724 深搜剪枝

    这道题目如果数据很小的话.我们通过这个dfs就可以完成深搜: void dfs(int s) { if (s==N) { minLen=min(minLen,totalLen); return ; } ...

  8. poj1190 生日蛋糕(深搜+剪枝)

    题目链接:poj1190 生日蛋糕 解题思路: 深搜,枚举:每一层可能的高度和半径 确定搜索范围:底层蛋糕的最大可能半径和最大可能高度 搜索顺序:从底层往上搭蛋糕,在同一层尝试时,半径和高度都是从大到 ...

  9. UVA 10160 Servicing Stations(深搜 + 剪枝)

    Problem D: Servicing stations A company offers personal computers for sale in N towns (3 <= N < ...

随机推荐

  1. [hdu6558][CCPC2018吉林D题]The Moon(期望dp)

    题目链接 当时年少不懂期望$dp$,时隔一年看到这道题感觉好容易.... 定义状态$dp[i]$表示当前的$q$值为$i$时的期望,则当$q$值为$100$时$dp[100]=100/q$,这时后发现 ...

  2. 一个阿里云apache服务器配置两个或多个域名forLinux

    一个阿里云apache服务器配置两个或多个域名for Linux: 默认已经配置好了阿里云提供的一键web安装,可以参考:http://www.42iot.com/?id=8 修改/alidata/s ...

  3. 让Elasticsearch飞起来!——性能优化实践干货

    原文:让Elasticsearch飞起来!--性能优化实践干货 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog ...

  4. VS Code 设置双快捷键(快速移动光标)

    平时写代码会经常用到上下左右键,比如打出两个括号 () ,编辑完之后得按到右括号后面 难免有这样的场景需要在编辑代码的时候小范围地移动光标,笔者在别的ide的习惯是通过“alt + jkli”来实现光 ...

  5. 编辑器和IDE的区别以及WebStorm和PhpStorm的区别

    编辑器和IDE的区别: 编辑器就是纯粹编辑文本的编辑器,识别级别在文字级,只显示你想写入和打开的文本内容,不管你写什么内容,只提供接收与显示功能,Windows自带的写字板就是最简单的编辑器.举个生活 ...

  6. 361-基于6U VPX TMS320C6678+XC7K325T 的信号处理板

    基于6U VPX TMS320C6678+XC7K325T 的信号处理板 一.板卡概述 本板卡基于6U VPX结构设计无线电信号处理平台.板卡包含1片C6678芯片,1片 FPGA XC7K325T- ...

  7. tmux 操作简版

    操作session: 操作window: 操作pane: 原文

  8. 使用BaGet来管理内部Nuget包

    有的时候,我们想要制作一些nuget包,供自己内部调用,不想公开出去,那么就需要一个能够集中管理nuget包的服务了,今天我来给大家介绍一款轻便好用的包管理服务-----BaGet 下载并部署BaGe ...

  9. Maven生成可以直接运行的jar包的多种方式(转)

    转自:https://blog.csdn.net/xiao__gui/article/details/47341385 Maven可以使用mvn package指令对项目进行打包,如果使用java - ...

  10. 610K图纸打印新版增值税发票不完整的调整方法 黑盘红盘都兼容

    新版增票页面设置增票向下0.8向右-10,5刻度进纸测试 向右调整可能会有些出入 根据情况微调即可. 下面为黑盘的设置 可与上面兼容