建议入门的人先看cd琦的《基于连通性状态压缩的动态规划问题》。事半功倍。

插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类。

插头DP建议先理解“插头”的概念。然后会HASH表(这个其实是很基础的东西,应该都会的)。然后就是DP。

以及特殊题目的特殊处理。

好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数。

大体上每个题说几句特殊处理,有问题请纠正。。。。题目的顺序基本上难度递增

另外代码我都是用括号匹配的。因为感觉连通块编号不好。(欢迎指教讨论)

有一点想特别提出来的,插头DP的时候,当left==2 && up==1,即在i,j,cur的括号匹配为()时,说明回路闭合。这点在一些题里都有体现。

注意:

一、多条回路的题一般可以用1位表示有无插头。

二、单条回路的题,闭合时要判断是否合法。

三、单条通路的题,个人建议特判。

四、转移过程中,要记录特殊的限制条件。

五、有待补充

HDU 1693 - Eat the Trees

给一个网格图,有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(可多条回路)的方案数。

题解:用1位来表示有无插头即可,因为这题是可以多回路,所以没必要记录括号类型或者是连通块编号。我用表示括号类型或表示连通块编号的时候TLE。。。

其实这题不需要记录括号类型、连通块编号。所以其实可以不用这么冗长的代码。直接dp也可。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010 int maze[maxd][maxd],code[maxd];
int n,m;
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
ll f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,ll ans){
int h=st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(state[i]==st){
f[i]+=ans;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i) code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i) ret = ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i) code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = (j==m?:);
int tmp=(<<((m+)-mv))-;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(left && up){
code[j-]=code[j]=;
if(j==m) shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}else if(left || up){
if(i+<=n && maze[i+][j]==){
code[j-]=,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(j+<=m && maze[i][j+]==){
code[j-]=,code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}else {
if(i+<=n && j+<=m && maze[i+][j]== && maze[i][j+]==){
code[j-]=code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}
}
}
void solve(){
int cur=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]==) dpblock(i,j,cur);
else dpblank(i,j,cur);
cur^=;
}
}
ll ans=;
for(int k=;k<hm[cur].sz;++k) ans+=hm[cur].f[k];
printf("There are %I64d ways to eat the trees.\n",ans);
}
int main(){
int t,ca=;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)for(int j=;j<=m;++j)scanf("%d",maze[i]+j);
printf("Case %d: ",++ca);
solve();
}
return ;
}

HDU 1693

URAL 1519 - Formula 1

给一个网格图,有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(单条回路)的方案数。

题解:用2位表示括号类型,闭合时判断是否为合法闭合处(最后一个可走的格子)。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010
char maze[maxd][maxd];
int code[maxd];
int n,m;
int ox,oy;
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
ll f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,ll ans){
int h=st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]+=ans;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret = ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = (j==m?:);
int tmp=(<<(*(m+)-mv))-;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
}
ll ans;
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(left && up){
if(left==&&up==){
if(i!=ox||j!=oy)continue;
code[j-]=code[j]=;
ans+=hm[cur].f[k];
continue;
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
else if(left || up){
int t = left|up;
if(i+<=n && maze[i+][j]=='.'){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(j+<=m && maze[i][j+]=='.'){
code[j-]=,code[j]=t;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}
else {
if(i+<=n && maze[i+][j]=='.' && j+<=m && maze[i][j+]=='.'){
code[j-]=,code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}
}
}
void solve(){
int cur=;
hm[].clear();
hm[].push(,);
ans=;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]=='*')dpblock(i,j,cur);
else dpblank(i,j,cur);
cur^=;
}
}
printf("%I64d\n",ans);
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=;i<=n;++i)scanf("%s",maze[i]+);
ox=oy=-;
for(int i=;i<=n;++i)for(int j=;j<=m;++j)if(maze[i][j]=='.')ox=i,oy=j;
if(ox==-){puts("");continue;}
solve();
}
return ;
}

URAL 1519

FZU 1977 - Pandora adventure

给一个网格图,有1.可走可不走 2.不可走 3.必须走 3种类型。要求必须走的必须全部都走完且仅走一次,可走的格子最多经过一次。输出回路(单条回路)的方案数。

题解:用2位表示括号类型,将可走可不走和必须走的归为一类,可走可不走只有当没有左插且没有上插的时候,才有机会变成不走的情况,其他情况都是必须走(因为要把插头搞掉)。闭合时判断是否为合法闭合处(必须走的格子都出现完了,即当前格子序>=最后一个必须走的格子的格子序)。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010 char maze[maxd][maxd];
int code[maxd];
int n,m;
int ox,oy;
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
ll f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,ll ans){
int h=st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]+=ans;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret = ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){// 'X'
int mv = (j==m?:);
int tmp=(<<(*(m+)-mv))-;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
}
ll ans;
void dpblank(int i,int j,int cur){// '*' or 'O'
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(left && up){
if(left==&&up==){
code[j-]=code[j]=;
if(encode()==){
if((i==ox && j>=oy) || i>ox)
ans += hm[cur].f[k];
}
continue;
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}else if(left || up){
int t = left|up;
if(i+<=n && maze[i+][j]!='X'){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(j+<=m && maze[i][j+]!='X'){
code[j-]=,code[j]=t;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}else {
if(maze[i][j]=='*'){// empty
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(i+<=n && maze[i+][j]!='X' && j+<=m && maze[i][j+]!='X'){// fill
code[j-]=,code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}
}
}
ll solve(){
int cur=;
hm[].clear();
hm[].push(,);
ans=;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]!='X')dpblank(i,j,cur);
if(maze[i][j]=='X')dpblock(i,j,cur);
cur^=;
}
}
return ans;
}
int main(){
int t,ca=;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)scanf("%s",maze[i]+);
ox=oy=n+;
for(int i=;i<=n;++i)for(int j=;j<=m;++j)if(maze[i][j]=='O')ox=i,oy=j;
printf("Case %d: %I64d\n",++ca,ox==n+?:solve());
}
return ;
}

FZU 1977

HDU 1964 - Pipes

给一个网格图,只有1种类型的格子,格子之间有边权,要求用一条回路遍历所有的格子仅一次。输出最小边权和(单条回路)。

题解:用2位表示括号类型,读入数据的时候我是用getchar读的。这题可以不用写dpblock。wall[i][j][0],wall[i][j][1]分别表示格子(i,j)的右边的边权、下边的边权。闭合时判断是否为合法闭合处(i==n&&j==m)。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010
#define inf 0x3f3f3f3f int wall[maxd][maxd][];
int n,m;
int code[maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
int f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,int ans){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i] = min(f[i], ans);
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret=ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(left&&up){
if(left==&&up==){
code[j-]=code[j]=;
if(i!=n||j!=m)continue;
shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]);
}else if(left||up){
int t = left|up;
if(i+<=n){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+wall[i][j][]);
}
if(j+<=m){
code[j-]=,code[j]=t;
hm[cur^].push(encode(), hm[cur].f[k]+wall[i][j][]);
}
}else {
if(i+<=n && j+<=m){
code[j-]=,code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]+wall[i][j][]+wall[i][j][]);
}
}
}
}
int solve(){
int cur=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
dpblank(i,j,cur);
cur^=;
}
}
int ans=inf;
for(int k=;k<hm[cur].sz;++k)
ans = min(ans, hm[cur].f[k]);
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);getchar();
memset(wall,,sizeof(wall));
char s[];
scanf("%s",s);
char cc;
cc=getchar();// '\n'
for(int i=;i<=n;++i){
cc=getchar();// '#'
for(int j=;j<m;++j){
cc=getchar();// ' '
cc=getchar();
wall[i][j][]=cc-'';
}
cc=getchar();// ' '
cc=getchar();// '#'
cc=getchar();// '\n'
if(i==n){scanf("%s",s);break;}
cc=getchar();// '#'
for(int j=;j<=m;++j){
cc=getchar();
wall[i][j][]=cc-'';
cc=getchar();// '#'
}
cc=getchar();// '\n'
}
printf("%d\n",solve());
}
return ;
}

HDU 1964

HDU 3377 - Plan

给一个网格图,只有1种类型的格子,格子有点权,现从左上角走到右下角,每个格子最多走一次,可以不走完所有格子。输出最大点权和(单条通路)。

题解:用2位表示括号类型,这题也不用写dpblock。转移过程中,要保证不闭合(left==2&&up==1时直接continue)。右下角(终点)特判有且仅有一个方向即可。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010
#define inf 0x3f3f3f3f int n,m;
int code[maxd];
int val[maxd][maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
int f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,int ans){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]=max(f[i], ans);
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret=ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
int ans;
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(i==n&&j==m){
if((left || up) && !(left&&up))
ans=max(ans, hm[cur].f[k]+val[i][j]);
continue;
}
if(left && up){
if(left==&&up==)
continue;
else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+val[i][j]);
}else if(left || up){
int t = left|up;
if(i+<=n){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+val[i][j]);
}
if(j+<=m){
code[j-]=,code[j]=t;
hm[cur^].push(encode(), hm[cur].f[k]+val[i][j]);
}
}else {
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]);
if(i+<=n && j+<=m){
code[j-]=,code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]+val[i][j]);
}
}
}
}
int solve(){
int cur=;
hm[].clear();
hm[].push((<<(*(m+)-)), );
ans=-inf;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
dpblank(i,j,cur);
cur^=;
}
}
return ans;
}
int main(){
int ca=;
while(~scanf("%d%d",&n,&m)){
for(int i=;i<=n;++i)for(int j=;j<=m;++j)scanf("%d",val[i]+j);
printf("Case %d: %d\n",++ca,solve());
}
return ;
}

HDU 3377

POJ 1739 - Tony's Tour

给一个网格图,格子有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出从左下角走到右下角的通路(单条通路)的方案数。

题解:用2位表示括号类型。转移过程中,要保证不闭合(left==2&&up==1时直接continue)。左下角(起点)和右下角(终点)特判处理即可。

一般而言,求通路的题也可以扩大网格图,转换成求回路数。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010 int n,m;
char maze[maxd][maxd];
int code[maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
ll f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,ll ans){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]+=ans;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret=ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = (j==m?:);
int tmp = (<<(*(m+)-mv))-;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
}
ll ans;
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(i==n&&j==){
if(up){
code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}else {
code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}
continue;
}
if(i==n&&j==m){
if((left || up) && !(left&&up))
ans+=hm[cur].f[k];
continue;
}
if(left && up){
if(left==&&up==)continue;
else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]);
}else if(left || up){
int t = left | up;
if(i+<=n && maze[i+][j]=='.'){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]);
}
if(j+<=m && maze[i][j+]=='.'){
code[j-]=,code[j]=t;
hm[cur^].push(encode(), hm[cur].f[k]);
}
}else {
if(i+<=n &&j+<=m && maze[i+][j]=='.' && maze[i][j+]=='.'){
code[j-]=,code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}
}
}
}
ll solve(){
int cur=;
hm[].clear();
hm[].push(,);
ans=;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]=='.') dpblank(i,j,cur);
else dpblock(i,j,cur);
cur^=;
}
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m) && n){
for(int i=;i<=n;++i)scanf("%s",maze[i]+);
if(m==){
int ans=;
for(int i=;i<n;++i)if(maze[i][]=='.')ans=;
if(maze[n][]=='#')ans=;
printf("%d\n",ans);
continue;
}
printf("%I64d\n",solve());
}
return ;
}

POJ 1739

POJ 3133 - Manhattan Wiring

给一个网格图,格子有1.可走可不走 2.不可走 3.标记"2" 4.标记"3" 4种类型。且3、4类型各有两个格子。问将2和3分别连起来(2和3的线不可相交)的最小曼哈顿距离和。

题解:用2位表示网格填充类型。00表示空,01表示"2"的线,10表示"3"的线。注意特判标记"2","3"的四个位置,具体看代码的特判部分。

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010
#define inf 0x3f3f3f3f int n,m,maze[maxd][maxd];
int code[maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
int f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,int ans){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]=min(f[i],ans);
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i)ret=ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = (j==m?:);
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push(hm[cur].state[k]>>mv, hm[cur].f[k]);
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(maze[i][j]>=){
if(left && up)continue;
if(left || up){
int t = (maze[i][j]==?:);
if(left+up!=t)continue;
code[j-]=code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}else {
int t = (maze[i][j]==?:);
if(i+<=n && maze[i+][j]!=){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(j+<=m && maze[i][j+]!=){
code[j-]=,code[j]=t;
hm[cur^].push(encode(),hm[cur].f[k]);
}
}
continue;
}
if(left && up){
if(left!=up)continue;
code[j-]=code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]+);
}else if(left || up){
int t = left | up;
if(i+<=n && maze[i+][j]!=){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]+);
}
if(j+<=m && maze[i][j+]!=){
code[j-]=,code[j]=t;
hm[cur^].push(encode(),hm[cur].f[k]+);
}
}else {
if(maze[i][j]==){
if(j==m)shift();
hm[cur^].push(encode(),hm[cur].f[k]);
}
if(i+<=n && j+<=m && maze[i+][j]!= && maze[i][j+]!=){
code[j-]=code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]+);
code[j-]=code[j]=;
hm[cur^].push(encode(),hm[cur].f[k]+);
}
}
}
}
int solve(){
int cur=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]==)dpblock(i,j,cur);
else dpblank(i,j,cur);
cur^=;
}
}
int ans=inf;
for(int k=;k<hm[cur].sz;++k)ans = min(ans, hm[cur].f[k]);
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m) && n){
for(int i=;i<=n;++i)for(int j=;j<=m;++j)scanf("%d",maze[i]+j);
int ans = solve();
printf("%d\n",ans==inf?:ans+);
}
return ;
}

POJ 3133

============================接下来4题个人感觉比上边的难==================================================

ZOJ 3466 - The Hive II

给一个六边形的网格图,格子有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(可多条回路)的方案数。

题解:基本思路跟HDU 1693一样。只是处理六边形比四边形麻烦得多。这题应该是可以不用HASH表,可以直接dp的。因为不需要记录括号类型、连通块编号。

处理六边形的时候,设某个平放(上下是平,左右是凸的)六边形位置为(x,y),则相邻的六边形位置分别为(x+1,y+1),(x+1,y-1),(x-1,y+1),(x-1,y-1),(x,y+1),(x,y-1)。

我用1位表示有无插头,总共有(2×m+1位)每次DP的时候有3个插头。1个左插,2个上插。我是一列列来DP的,注意换列的时候,奇列变偶列的时候要平移2位,偶列变奇列的时候不用平移。

(补充示意图==1.六边形位置 2.插头位置 3.DP顺序 4.换列平移)

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 33
#define HASH 10007
#define STATE 500010 int n,m,code[maxd];
int maze[maxd][maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
ll f[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,ll ans){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i]){
f[i]+=ans;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m*;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=*m;++i)ret = ret<<|code[i];
return ret;
}
void shift(){
for(int i=*m;i>=;--i)code[i]=code[i-];
code[]=code[]=;
}
void dpblock(int i,int j,int cur){
int mv = ;
if((j/+==m) && (i&)) mv=;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push(hm[cur].state[k]>>mv, hm[cur].f[k]);
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int jj = j/+;
int j1 = *(jj-),j2=j1+,j3=j2+;
int left = code[j1], up1 = code[j2], up2 = code[j3];
int sum = left+up1+up2;
if(sum==)continue;
if(sum==){
code[j1]=code[j2]=code[j3]=;
if(jj==m && (i&))shift();
hm[cur^].push(encode(), hm[cur].f[k]);
continue;
}
bool down1 = (i+<=n && j->= && maze[i+][j-]==);
bool down2 = (i+<=n && j+<*m && maze[i+][j+]==);
bool right = (j+<*m && maze[i][j+]==);
if(sum==){
if(down1){
code[j1]=,code[j2]=code[j3]=;
if(jj==m && (i&))shift();
hm[cur^].push(encode(), hm[cur].f[k]);
decode(hm[cur].state[k]);
}
if(down2){
code[j1]=,code[j2]=,code[j3]=;
if(jj==m && (i&))shift();
hm[cur^].push(encode(), hm[cur].f[k]);
decode(hm[cur].state[k]);
}
if(right){
code[j1]=code[j2]=,code[j3]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}
continue;
}
if(right){
if(down1){
code[j1]=,code[j2]=,code[j3]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}
if(down2){
code[j1]=,code[j2]=code[j3]=;
hm[cur^].push(encode(), hm[cur].f[k]);
}
}
if(down1 && down2){
code[j1]=code[j2]=,code[j3]=;
if(jj==m && (i&))shift();
hm[cur^].push(encode(), hm[cur].f[k]);
}
}
}
ll solve(){
int cur=;
hm[].clear();
hm[].push(,);
for(int i=;i<=n;++i){
for(int j=(i&);j<*m;j+=){
hm[cur^].clear();
if(maze[i][j]==)dpblock(i,j,cur);
else dpblank(i,j,cur);
cur^=;
}
}
ll ans=;
for(int k=;k<hm[cur].sz;++k) ans += hm[cur].f[k];
return ans;
}
int main(){
int q;
while(~scanf("%d%d",&m,&q)){
n=;
memset(maze,,sizeof(maze));
while(q--){
char tmp[];
scanf("%s",tmp);
int row = tmp[]-'A'+;
int col = tmp[]-'A'+;
if(row&) col = col* - ;
else col = col* - ;
maze[row][col]=;
}
printf("%lld\n",solve());
}
return ;
}

ZOJ 3466

ZOJ 3256 - Tour in the Castle

给一个网格图,格子只有必须走1种类型。必须走的必须全部走完且仅走一次。输出从左上走到左下的哈密顿通路的方案数。网格图为N×M,其中N<=7, M<=1e9 。

题解:注意到M很大,所以不可能按行列逐格DP。由于所有格子都是一样的,我们可以作出列与列之间的可转移关系。即某一列的上插状态(N个code[i])可推出下一列的哪些上插状态。然后构成一个矩阵A,其中A[i][j]表示第i个状态可转移到第j个状态。

为方便描述,现将图顺时针旋转90度。一开始的状态则是最左和最右都是有1个上插。然后由这个状态推出可能出现的所有状态。

当我们用一个邻接矩阵表示一个无权图的时候,令B=A^m,则B[i][j]表示的是i结点经过m步到达j结点的方案数。

这题由于N<=7,所以连通块编号最多是3(1个0,2个1,2个2,2个3),所以我用了表示连通块编号的方法而没采用表示括号类型的方法,其实都一样。

还有这题我没有用HASHMAP。因为这题在极端数据下(N=7)可达的状态也很少(不超过133,具体忘了多少了,可以试下输出那个v.size())。

而之前我们使用HASHMAP的一个很重要的原因是,HASHMAP每次清空是O(HASH),而那些题目需要多次清空HASHMAP(一般为N×M次),而本题对于每个N只需要清空1次,所以感觉直接用STL的map和vector配合应该就可以了。

需要注意的是,一开始我的矩阵快速幂的乘法的取模是放在最里层的,结果4520ms的AC,时限是5000ms。。好险,后来把取模放到第二层,瞬间940ms。(由于题目取模是7777777<8*1e6,所以((8*1e6)^2)*133不会爆lld)。

至此,题目大体解决。但是我一直很怀疑,可以直接用ans==0来判断Impossible吗?如果结果刚好是MOD的倍数,被MOD成0了怎么破,是要加标记吗?

请高手指点。。。。

(补充示意图==1.旋转后的图 2.插头位置)

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <vector>
using namespace std; #define ll long long
#define maxd 15
#define mod 7777777
#define mk 133// 阶数
struct Mat{
Mat(){memset(a,,sizeof(a));}
ll a[mk][mk];
};
Mat operator *(const Mat& a,const Mat& b){
Mat ret;
for(int i=;i<mk;++i)
for(int j=;j<mk;++j){
for(int k=;k<mk;++k){
ret.a[i][j]+=a.a[i][k]*b.a[k][j];
//ret.a[i][j]%=mod;
}
ret.a[i][j]%=mod;
}
return ret;
}
Mat operator ^(Mat x, int n){
Mat ret;
for(int i=;i<mk;++i) ret.a[i][i]=;
while(n){
if(n&)ret=ret*x;
x=x*x;
n>>=;
}
return ret;
} int n,m,code[maxd];
map<int,int>mp;
void decode(int st){
for(int i=n;i;--i)code[i]=st&,st>>=;
}
int encode(){
int cnt=;
int ch[];
memset(ch,-,sizeof(ch));
ch[]=cnt++;
int ret=;
for(int i=;i<=n;++i){
if(ch[code[i]]==-)ch[code[i]]=cnt++;
code[i]=ch[code[i]];
ret = ret<<|code[i];
}
return ret;
}
/*
6种插头:
0: —— or 左+上 or 右+上
1: | or 左+下 or 右+下
*/
bool check(int st,int nst){
decode(st);
int left=,cnt=,k;
for(int i=;i<=n;++i){
int nsti = nst&(<<(n-i));
if(left==){
if(code[i]== && nsti==) return false;
if(code[i]== && nsti) left = -;// nsti: 右+下
if(code[i] && nsti==) left = code[i];// nsti: 右+上
if(code[i] && nsti) continue;// nsti: |
k=i;// 上一个待修改插头
}
else {
if(code[i]== && nsti==) continue;// nsti: ——
else if(code[i]== && nsti){
if(left>) code[k]=,code[i]=left;// nstk: 右+上 nsti: 左+下
else code[k]=code[i]=+(cnt++);// nstk: 右+下 nsti: 左+下
left = ;
}
else if(code[i] && nsti==){
if(code[i]==left) if(nst!= || i!=n) return false;// code[i]==left说明闭合。回路闭合的判断
if(left>){// nstk: 右+上 nsti: 左+上
for(int j=;j<=n;++j) if(code[j]==left) code[j]=code[i];
code[k]=code[i]=;left=;
}else {// nstk: 右+下 nsti: 左+上
code[k]=code[i],code[i]=;left=;
}
}
else if(code[i] && nsti) return false;
}
}
return left==;
}
Mat A[];
bool vis[];
void init(){
if(vis[n]) return ;
vis[n]=true;
memset(code,,sizeof(code));
code[]=code[n]=;
map<int,int>mp;
vector<int>v;
mp[]=;mp[encode()]=;
v.push_back();v.push_back(encode());
int sz=;
for(int i=;i<v.size();++i){
int st = v[i], idx = i;
for(int nst=;nst<(<<n);++nst){
if(check(st,nst)){
int st2 = encode();
map<int,int>::iterator it = mp.find(st2);
if(it==mp.end()){
A[n].a[idx][sz]=;
mp[st2] = sz++;
v.push_back(st2);
}else A[n].a[idx][it->second]=;
}
}
}
}
int main(){
for(int i=;i<;++i)memset(A[i].a,,sizeof(A[i].a));
memset(vis,false,sizeof(vis));
while(~scanf("%d%d",&n,&m)){
init();
Mat ans = A[n]^m;
if(ans.a[][]==)puts("Impossible");
else printf("%d\n",ans.a[][]);
}
return ;
}

ZOJ 3256

ZOJ 3213 - Beautiful Meadow

给一个网格图,格子有可走可不走和不可走2种类型。格子有点权。输出简单路径(可以从任何一个可走可不走的格子开始或结束,每个格子最多走一次)经过格子点权和最大的点权和。

题解:本题用3位表示连通块编号,因为存在独立插头(括号?连通块?),即头尾的单插头,即比如当一条路径为竖直时,插头状态会类似于0001000。 所以本来N,M<=7应该是连通块编号最多为3 ,但是这里可能连通块编号为4 ,所以要用3位。转移过程中,记录头尾的个数(独立插头的个数)。必须保证endcount<=2。最后的答案就是所有endcount==2(有头有尾)的f[k]的和。

看来还是表示连通块编号的方法适用范围比较大。。。

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010 int n,m,code[maxd],endc;
int maze[maxd][maxd];
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
int f[STATE];
int edc[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,int ans,int endc){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i] && endc==edc[i]){
f[i] = max(f[i], ans);
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;edc[sz]=endc;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ch[maxd],sz=,ret=;
memset(ch,-,sizeof(ch));
ch[]=sz++;
for(int i=;i<=m;++i){
if(ch[code[i]]==-) ch[code[i]]=sz++;
code[i]=ch[code[i]];
ret = ret<<|code[i];
}
return ret;
}
void shift(){
for(int i=m;i;--i) code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = j==m?:;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push(hm[cur].state[k]>>mv, hm[cur].f[k], hm[cur].edc[k]);
}
int ans;
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
int endc = hm[cur].edc[k];
int sum=;
for(int i=;i<=m;++i)if(code[i])++sum;
if(sum+endc<=) ans = max(ans, hm[cur].f[k]);
if(endc== && sum==)continue;
if(left && up){
if(left == up) continue;
for(int jj=;jj<=m;++jj)
if(code[jj]==left) code[jj]=up;
code[j-]=code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc);
}else if(left || up){
int t = left | up;
if(endc<){
int tmp[maxd];memcpy(tmp,code,sizeof(tmp));
code[j-]=code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc+);
memcpy(code,tmp,sizeof(tmp));
}
if(i+<=n && maze[i+][j]){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc);
}
if(j+<=m && maze[i][j+]){
code[j-]=,code[j]=t;
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc);
}
}else {
if(i+<=n && maze[i+][j] && j+<=m && maze[i][j+]){
code[j-]=code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc);
code[j-]=code[j]=;
}
if(endc< && i+<=n && maze[i+][j]){
int tmp[maxd];memcpy(tmp,code,sizeof(tmp));
code[j-]=,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc+);
memcpy(code,tmp,sizeof(tmp));
}
if(endc< && j+<=m && maze[i][j+]){
code[j-]=,code[j]=;
hm[cur^].push(encode(), hm[cur].f[k]+maze[i][j], endc+);
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k], endc);
}
}
}
int solve(){
int cur=;
hm[].clear();
hm[].push(,,);
ans=;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
ans = max(ans, maze[i][j]);
hm[cur^].clear();
if(maze[i][j]) dpblank(i,j,cur);
else dpblock(i,j,cur);
cur^=;
}
}
for(int k=;k<hm[cur].sz;++k)
//if(hm[cur].edc[k]==2)
ans = max(ans, hm[cur].f[k]);
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)for(int j=;j<=m;++j)scanf("%d",maze[i]+j);
printf("%d\n",solve());
}
return ;
}

ZOJ 3213

HDU 4285

给一个网格图,格子有必须走和不可走2种类型。必须走的必须全部走完且仅走一次。输出构成K个回路,且回路间不嵌套包含的方案数。

题解:这题我用2位表示括号类型。首先,要记录当前状态构成的回路数,这个是肯定的,然后把K个回路的方案数加起来就是答案了。问题是,怎么保证不嵌套包含。这里我一开始也WA了,后来参考网上机智做法,当合并结点(i,j)的左(0~j-2)右(j+1~m)的插头个数都是偶数时,则没有嵌套包含。这个想一想还是可以想象的,不过具体证明不知道应该怎么证明了。。。然后就顺利AC了,不过数据有点大,8484+ms

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define ll long long
#define maxd 15
#define HASH 10007
#define STATE 500010
#define MOD 1000000007 int n,m,code[maxd];
char maze[maxd][maxd];
int K;
struct HASHMAP{
int head[HASH];
int state[STATE],nxt[STATE];
int f[STATE];
int cnt[STATE];
int sz;
void clear(){sz=;memset(head,-,sizeof(head));}
void push(int st,int ans,int cc){
int h = st%HASH;
for(int i=head[h];i!=-;i=nxt[i]){
if(st==state[i] && cc==cnt[i]){
f[i]=(f[i]+ans)%MOD;
return ;
}
}
state[sz]=st,nxt[sz]=head[h],f[sz]=ans;cnt[sz]=cc;
head[h]=sz++;
}
}hm[];
void decode(int st){
for(int i=m;i>=;--i)code[i]=st&,st>>=;
}
int encode(){
int ret=;
for(int i=;i<=m;++i) ret = ret<<|code[i];
return ret;
}
void shift(){
for(int i=m;i;--i)code[i]=code[i-];
code[]=;
}
void dpblock(int i,int j,int cur){
int mv = j==m?:;
for(int k=;k<hm[cur].sz;++k)
hm[cur^].push(hm[cur].state[k]>>mv, hm[cur].f[k],hm[cur].cnt[k]);
}
void dpblank(int i,int j,int cur){
for(int k=;k<hm[cur].sz;++k){
decode(hm[cur].state[k]);
int left = code[j-], up = code[j];
if(left && up){
if(left==&&up==){
if(hm[cur].cnt[k]==K)continue;
int tmp=;
for(int jj=j-;jj>=;--jj)if(code[jj])++tmp;
if(tmp&)continue;
for(int jj=j+;jj<=m;++jj)if(code[jj])++tmp;
if(tmp&)continue;
code[j-]=code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k], hm[cur].cnt[k]+);
continue;
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j+,tmp=;jj<=m;++jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else if(left==&&up==){
code[j-]=code[j]=;
for(int jj=j-,tmp=;jj>=;--jj){
if(code[jj]==)++tmp;
if(code[jj]==)--tmp;
if(tmp==){code[jj]=-code[jj];break;}
}
}else {
code[j-]=code[j]=;
}
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
}else if(left || up){
int t = left | up;
if(i+<=n && maze[i+][j]=='.'){
code[j-]=t,code[j]=;
if(j==m)shift();
hm[cur^].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
}
if(j+<=m && maze[i][j+]=='.'){
code[j-]=,code[j]=t;
hm[cur^].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
}
}else {
if(i+<=n && j+<=m && maze[i+][j]=='.' && maze[i][j+]=='.'){
code[j-]=,code[j]=;
hm[cur^].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
}
}
}
}
int solve(){
int cur=;
hm[].clear();
hm[].push(,,);
for(int i=;i<=n;++i){
for(int j=;j<=m;++j){
hm[cur^].clear();
if(maze[i][j]=='.')dpblank(i,j,cur);
else dpblock(i,j,cur);
cur^=;
}
}
int ans=;
for(int k=;k<hm[cur].sz;++k) if(hm[cur].cnt[k]==K) ans=(ans+hm[cur].f[k])%MOD;
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&K);
for(int i=;i<=n;++i)scanf("%s",maze[i]+);
printf("%d\n",solve());
}
return ;
}

HDU 4285

至此,插头DP的入门算是完成了。。。。

插头DP专题的更多相关文章

  1. [专题总结]初探插头dp

    彻彻底底写到自闭的一个专题. 就是大型分类讨论,压行+宏定义很有优势. 常用滚动数组+哈希表+位运算.当然还有轮廓线. Formula 1: 经过所有格子的哈密顿回路数. 每个非障碍点必须有且仅有2个 ...

  2. dp专题训练

    ****************************************************************************************** 动态规划 专题训练 ...

  3. 插头dp练习

    最近学了插头dp,准备陆续更新插头dp类练习. 学习论文还是cdq那篇<基于连通性状态压缩的动态规划问题>. 基本的想法都讲得很通透了,接下来就靠自己yy了. 还有感谢kuangbin大大 ...

  4. 插头dp

    插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...

  5. HDU 4113 Construct the Great Wall(插头dp)

    好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...

  6. HDU 4949 Light(插头dp、位运算)

    比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...

  7. HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)

    插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...

  8. HDU 1693 Eat the Trees(插头DP)

    题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...

  9. HDU 4064 Carcassonne(插头DP)(The 36th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4064 Problem Description Carcassonne is a tile-based ...

随机推荐

  1. recording just for inquiry in the future

    auditd审计 相关命令有: auditd, auditctl, ausearch, aureport 相关文件: /etc/audit/auditd.conf, /etc/audit/audit. ...

  2. 性能:15个JavaScript本地存储技术的函数库和工具

    当构建更复杂的JavaScript应用程序运行在用户的浏览器是非常有用的,它可以在浏览器中存储信息,这样的信息可以被共享在不同的页面,浏览会话. 在最近的过去,这将有可能只被cookies文本文件保存 ...

  3. LINUX下搭建VPN

    一.准备 需要 dkms-2.0.17.5-1.noarch.rpm.ppp-2.4.5-33.0.rhel6.x86_64.rpm.pptpd-1.4.0-1.el6.x86_64.rpm,并依次安 ...

  4. Jquery网页加载进度条(随笔,当然要随便写,当日记动态心情写咯)

    首先先是吐槽时间... 告诉大家一个好消息,就是有个妹子非常仰慕我的前端技术说要包养我 然后有好多羡慕嫉妒恨的童鞋一定要说,少年你太天真了,那一定是HR 然后我表示她不是HR,本宅的春天貌似要到来了. ...

  5. C++你不知道的那些事儿—C++语言的15个晦涩特性

    这个列表收集了 C++ 语言的一些晦涩(Obscure)特性,是我经年累月研究这门语言的各个方面收集起来的.C++非常庞大,我总是能学到一些新知识.即使你对C++已了如指掌,也希望你能从列表中学到一些 ...

  6. 懒加载的用处和赋nil操作[iOS开发教程]

    懒加载的用处和赋nil操作 1:数据,清空操作: self.array = nil; 2:归档从新从本地获取数据 self.archive = nil; ##id = nil的用处 block当参数, ...

  7. List对象排序通用方法

    import java.util.Collections; import java.util.Comparator; import java.util.List; import java.lang.r ...

  8. 在Mac mini上编译Android源码

    参考文章 1.Android 6.0 源代码编译实践 2.编译Android源码致命错误解决方案 实践过程 1.Mac下安装Ubuntu双系统 (1)Ubuntu版本:Ubuntu 15.10 注:实 ...

  9. thinkphp的CURD操作

    增 //a字段是主键 $data['b'] = 'bbb'; $data['c'] = 'c'; $new_id = M('test')->data($data)->add(); //ec ...

  10. 随机添加一个Class,Class提前写好

    $("").hover(function(){ var ary = ["red","green","blue",]; v ...