插头dp练习
最近学了插头dp,准备陆续更新插头dp类练习。
学习论文还是cdq那篇《基于连通性状态压缩的动态规划问题》。
基本的想法都讲得很通透了,接下来就靠自己yy了。
还有感谢kuangbin大大的专题练习。
首先入门肯定写个n*m*state的插头dp,没为什么,这东西好写啊~
然后你就想着,当n*m特别大的时候我该怎么解决呢(m不可能特别大,因为他决定了状态数state)。
你会发现这个dp只针对前一种状态和后一种状态进行转移。
所以你能最后把他压缩成2*state的插头dp,当然这么写肯定是很麻烦的写法,但是极为节省空间。
首先是基础题:
Eat the Trees
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4168 Accepted Submission(s): 2091
So Pudge’s teammates give him a new assignment—Eat the Trees!
The trees are in a rectangle N * M cells in size and each of the cells either has exactly one tree or has nothing at all. And what Pudge needs to do is to eat all trees that are in the cells.
There are several rules Pudge must follow:
I. Pudge must eat the trees by choosing a circuit and he then will eat all trees that are in the chosen circuit.
II. The cell that does not contain a tree is unreachable, e.g. each of the cells that is through the circuit which Pudge chooses must contain a tree and when the circuit is chosen, the trees which are in the cells on the circuit will disappear.
III. Pudge may choose one or more circuits to eat the trees.
Now Pudge has a question, how many ways are there to eat the trees?
At the picture below three samples are given for N = 6 and M = 3(gray square means no trees in the cell, and the bold black line means the chosen circuit(s))
For each case, the first line contains the integer numbers N and M, 1<=N, M<=11. Each of the next N lines contains M numbers (either 0 or 1) separated by a space. Number 0 means a cell which has no trees and number 1 means a cell that has exactly one tree.
6 3
1 1 1
1 0 1
1 1 1
1 1 1
1 0 1
1 1 1
2 4
1 1 1 1
1 1 1 1
基础题嘛,很容易想到转移方程。鉴于我嫌麻烦,写了空间复杂度较高的代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define LL long long
using namespace std;
LL dp[][][<<+];
int mp[][];
int main()
{
int T,n,m,k,t1,t2,kase=;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&mp[i][j]);
clr(dp);
dp[][m][]=;
for(int i=;i<=n;i++)
{
for(int k=;k<(<<m);k++)
{
dp[i][][k<<]=dp[i-][m][k];
}
for(int j=;j<=m;j++)
{
for(int k=;k<(<<(m+));k++)
{
t2=<<j;
t1=<<(j-);
if(mp[i][j])
{
if(((k&t2)== && (k&t1)!=) || ((k&t2)!= && (k&t1)==))
dp[i][j][k]+=dp[i][j-][k];
dp[i][j][k]+=dp[i][j-][k^t1^t2];
}
else
{
if((k&t1)==&&(k&t2)==)dp[i][j][k]+=dp[i][j-][k];
}
}
}
}
printf("Case %d: There are %lld ways to eat the trees.\n",++kase,dp[n][m][]);
}
return ;
}
插头dp
Pandora adventure
Problem Description
The pollution of the earth is so serious that people can not survive any more. Fortunately, people have found a new planet that maybe has life, and we call it "Pandora Planet".
Leonardo Da Vinci is the only astronaut on the earth. He will be sent to the Pandora Planet to gather some plant specimens and go back. The plant specimen is important to the people to decide whether the planet is fit to live or not.
Assuming that Da Vinci can only move in an N×M grid. The positions of the plant specimens he wants to collect are all marked by the satellite. His task is to find a path to collect all the plant specimens and return to the spaceship. There are some savage beasts in the planet. Da Vinci can not investigate the grid with the savage beast. These grids are also marked by the satellite. In order to save time Da Vinci could only visit each grid exactly once and also return to the start grid, that is, you can not visit a grid twice except the start grid. You should note that you can choose any grid as the start grid.
Now he wants to know the number of different paths he can collect all the plant specimens. We only care about the path and ignore where the start grid is, so the two paths in Figure 1 are considered as the same.
Figure 1
Input
The first line of the input contains an integer T (T≤100), indicating the number of cases. Each case begins with a line containing two integers N and M (1≤N, M≤12), the size of the planet is N×M. Each of the following N lines contains M characters Gij(1≤i≤N, 1≤j≤M), Gij denotes the status of the grid in row i and column j, where 'X' denotes the grid with savage beast, '*' denotes the safe grid that you can decide to go or not, 'O' denotes the plant specimen you should collect. We guarantee that there are at least three plant specimens in the map.
Output
For each test case, print a line containing the test case number (beginning with 1) and the number of different paths he can collect all the plant specimens. You can make sure that the answer will fit in a 64-bit signed integer.
Sample Input
Sample Output
三种格子:必经过,不能经过,经不经过无所谓。然后求只有一条的欧拉回路。
#include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define LL long long
#define HASH 10007
#define STATE 1000010
using namespace std;
struct hashmap
{
int size;
int next[STATE],state[STATE],head[HASH];
LL fas[STATE];
void init()
{
size=;
clr_1(head);
}
void add(int st,LL solu)
{
int k=st%HASH;
for(int i=head[k];i!=-;i=next[i])
if(st==state[i])
{
fas[i]+=solu;
return;
}
next[++size]=head[k];
fas[size]=solu;
state[size]=st;
head[k]=size;
return ;
}
}dp[];
int maped[][],endflag;
char s[];
int code[];
void init(int n,int m)
{
clr(maped);
for(int i=;i<=n;i++)
{
scanf("%s",s);
for(int j=;j<m;j++)
{
if(s[j]=='*')
maped[i][j+]=;
if(s[j]=='O')
maped[i][j+]=;
}
}
return ;
}
void decode(int st,int *code,int m)
{
endflag=st&;
st>>=;
for(int i=m;i>=;i--)
{
code[i]=st&;
st>>=;
}
return ;
}
int encode(int *code,int m)
{
int st=;
for(int i=;i<=m;i++)
{
st<<=;
st|=code[i];
}
st<<=;
st|=endflag;
return st;
}
void shift(int *code,int m)
{
for(int i=m;i>;i--)
code[i]=code[i-];
code[]=;
return ;
}
void dpblank(int i,int j,int cnt,int m)
{
int top,left,up;
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
left=code[j-];
up=code[j];
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
if(left && up)
{
if(left!=up)
{
code[j]=code[j-]=;
if(left==) endflag=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
else
{
if(left==)
{
top=;
for(int k=j-;k>=;k--)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
else
{
top=;
for(int k=j+;k<=m;k++)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
else if(left || up)
{
if(left) top=left;
else top=up;
if(maped[i][j+])
{
code[j-]=;
code[j]=top;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
if(maped[i+][j])
{
code[j-]=top;
code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
else
{
if(maped[i][j+] && maped[i+][j])
{
code[j-]=;
code[j]=;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
if(maped[i][j]==)
{
code[j-]=code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
}
return ;
}
void dpnone(int i,int j,int cnt,int m)
{
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
return ;
}
LL solve(int n,int m)
{
int cnt=;
LL ans=;
dp[cnt].init();
dp[cnt].add(,);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
dp[cnt^].init();
if(maped[i][j]==)
dpnone(i,j,cnt,m);
else
dpblank(i,j,cnt,m);
cnt^=;
/* for(int it=1;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
for(int k=0;k<=m;k++)
printf("%d:%d ",k,code[k]);
printf("FLAG:%d fas:%lld\n",endflag,dp[cnt].fas[it]);
}
printf("\n"); */
}
}
for(int i=;i<=dp[cnt].size;i++)
ans+=dp[cnt].fas[i];
return ans;
}
int main()
{
int T,n,m;
scanf("%d",&T);
for(int kase=;kase<=T;kase++)
{
scanf("%d%d",&n,&m);
init(n,m);
printf("Case %d: %lld\n",kase,solve(n,m));
}
return ;
}
Pipes
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 917 Accepted Submission(s): 448
Amodern office building ismade up of squaremodules, one on each floor being a service module from which (among other things) hot water is pumped out to the other modules through the heating pipes. Each module (including the service module) will have heating pipes connecting it to exactly two of its two to four neighboring modules. Thus, the pipes have to run in a circuit, from the service module, visiting each module exactly once, before finally returning to the service module. Due to different properties of the modules, having pipes connecting a pair of adjacent modules comes at different costs. For example, some modules are separated by thick walls, increasing the cost of laying pipes. Your task is to, given a description of a floor of an office building, decide the cheapest way to route the heating pipes.
4 3
#######
# 2 3 #
#1#9#1#
# 2 3 #
#1#7#1#
# 5 3 #
#1#9#1#
# 2 3 #
#######
4 4
#########
# 2 3 3 #
#1#9#1#4#
# 2 3 6 #
#1#7#1#5#
# 5 3 1 #
#1#9#1#7#
# 2 3 0 #
#########
2 2
#####
# 1 #
#2#3#
# 4 #
#####
45
10
#include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define LL long long
#define HASH 10007
#define STATE 1000010
using namespace std;
struct hashmap
{
int size;
int next[STATE],head[HASH];
LL state[STATE];
LL fas[STATE];
void init()
{
size=;
clr_1(head);
}
void add(LL st,LL solu)
{
int k=st%HASH;
for(int i=head[k];i!=-;i=next[i])
if(st==state[i])
{
if(fas[i]>solu)
fas[i]=solu;
return;
}
next[++size]=head[k];
fas[size]=solu;
state[size]=st;
head[k]=size;
return ;
}
}dp[];
int maped[][],endflag;
char s[];
int code[];
void init(int n,int m)
{
clr(maped);
fgets(s,,stdin);
for(int i=;i<=n;i++)
{
fgets(s,,stdin);
for(int j=;j<m;j++)
{
if(s[j]==' ')
maped[i][j+]=;
if(s[j]>='' && s[j]<='')
maped[i][j+]=s[j]-''+;
}
}
return ;
}
void decode(LL st,int *code,int m)
{
endflag=st&;
st>>=;
for(int i=m;i>=;i--)
{
code[i]=st&;
st>>=;
}
return ;
}
LL encode(int *code,int m)
{
LL st=;
for(int i=;i<=m;i++)
{
st<<=;
st|=code[i];
}
st<<=;
st|=endflag;
return st;
}
void shift(int *code,int m)
{
for(int i=m;i>;i--)
code[i]=code[i-];
code[]=;
return ;
}
void dpblank(int i,int j,int cnt,int m)
{
int top,left,up;
LL cost;
if(maped[i][j]==)
cost=;
else
cost=1LL*(maped[i][j]-);
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
left=code[j-];
up=code[j];
if(left && up)
{
if(left!=up)
{
code[j]=code[j-]=;
if(left==) endflag=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
else
{
if(left==)
{
top=;
for(int k=j-;k>=;k--)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
else
{
top=;
for(int k=j+;k<=m;k++)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else if(left || up)
{
if(left) top=left;
else top=up;
if(maped[i][j+])
{
code[j-]=;
code[j]=top;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
if(maped[i+][j])
{
code[j-]=top;
code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else
{
if(maped[i][j+] && maped[i+][j])
{
code[j-]=;
code[j]=;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
if(maped[i][j]!=)
{
code[j-]=code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
}
return ;
}
void dpnone(int i,int j,int cnt,int m)
{
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
return ;
}
LL solve(int n,int m)
{
int cnt=;
LL ans=;
dp[cnt].init();
dp[cnt].add(,);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
dp[cnt^].init();
if(maped[i][j]==)
dpnone(i,j,cnt,m);
else
dpblank(i,j,cnt,m);
cnt^=;
/* for(int it=1;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
for(int k=0;k<=m;k++)
printf("%d:%d ",k,code[k]);
printf("FLAG:%d fas:%lld\n",endflag,dp[cnt].fas[it]);
}
printf("\n"); */
}
}
for(int i=;i<=dp[cnt].size;i++)
ans+=dp[cnt].fas[i];
return ans;
}
int main()
{
int T,n,m;
scanf("%d",&T);
for(int kase=;kase<=T;kase++)
{
scanf("%d%d",&n,&m);
n=*n+;
m=*m+;
init(n,m);
printf("%lld\n",solve(n,m));
}
return ;
}
Plan
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1436 Accepted Submission(s): 517
Now they comes to the magical world and Lilish ask Resty to play with her.
The game is following :
Now the world is divided into a m * n grids by Lilith, and Lilith gives each grid a score.
So we can use a matrix to describe it.
You should come from cell(0, 0) to cell(m-1, n-1) (Up-Left to Down-Right) and try to colloct as more score as possible.
According to Lilish's rule, you can't arrive at each cell more than once.
Resty knows that Lilish will be easy to find the max score, and he doesn't want to lose the game.
So he want to find the game plan to reach the max score.
Your task is to calculate the max score that Lilish will find, the map is so small so it shouldn't be difficult for you, right?
Process to the END OF DATA.
For each test data :
the first live give m and n. (1<=m<=8, 1<=n<=9)
following m lines, each contain n number to give you the m*n matrix.
each number in the matrix is between -2000 and 2000
Don't print any empty line to the output
1 2
3 1
3 3
0 -20 100
1 -20 -20
1 1 1
Case 2: 61
这题仍然可以用回路做,value数组存经过的贡献,maped存是否可走(可走,不可走,必过)。
#include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define LL long long
#define HASH 10007
#define STATE 1000010
using namespace std;
struct hashmap
{
int size;
int next[STATE],head[HASH];
LL state[STATE];
LL fas[STATE];
void init()
{
size=;
clr_1(head);
}
void add(LL st,LL solu)
{
int k=st%HASH;
for(int i=head[k];i!=-;i=next[i])
if(st==state[i])
{
if(fas[i]<solu)
fas[i]=solu;
return;
}
next[++size]=head[k];
fas[size]=solu;
state[size]=st;
head[k]=size;
return ;
}
}dp[];
int maped[][],value[][],endflag;
char s[];
int code[];
void init(int &n,int &m)
{
clr(maped);
clr(value);
for(int i=;i<=n+;i++)
{
for(int j=;j<=m+;j++)
{
maped[i][j]=;
scanf("%d",&value[i][j]);
}
}
n+=;
m+=;
for(int i=;i<=n;i++)
maped[i][]=maped[i][m]=;
for(int i=;i<=m;i++)
maped[][i]=maped[n][i]=;
maped[][]=;
maped[n-][m-]=;
maped[][]=maped[n-][m-]=;
maped[n][m]=;
/* for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
printf("%d ",maped[i][j]);
printf("\n");
}*/
return ;
}
void decode(LL st,int *code,int m)
{
endflag=st&;
st>>=;
for(int i=m;i>=;i--)
{
code[i]=st&;
st>>=;
}
return ;
}
LL encode(int *code,int m)
{
LL st=;
for(int i=;i<=m;i++)
{
st<<=;
st|=code[i];
}
st<<=;
st|=endflag;
return st;
}
void shift(int *code,int m)
{
for(int i=m;i>;i--)
code[i]=code[i-];
code[]=;
return ;
}
void dpblank(int i,int j,int cnt,int m)
{
int top,left,up;
LL cost=1LL*value[i][j];
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
left=code[j-];
up=code[j];
if(left && up)
{
if(left!=up)
{
code[j]=code[j-]=;
if(left==) endflag=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
else
{
if(left==)
{
top=;
for(int k=j-;k>=;k--)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
else
{
top=;
for(int k=j+;k<=m;k++)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else if(left || up)
{
if(left) top=left;
else top=up;
if(maped[i][j+])
{
code[j-]=;
code[j]=top;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
if(maped[i+][j])
{
code[j-]=top;
code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else
{
if(maped[i][j+] && maped[i+][j])
{
code[j-]=;
code[j]=;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
if(maped[i][j]==)
{
code[j-]=code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
}
return ;
}
void dpnone(int i,int j,int cnt,int m)
{
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
if(endflag &&((dp[cnt].state[it]>>)> || maped[i][j]==)) continue;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
return ;
}
LL solve(int n,int m)
{
int cnt=;
LL ans=;
dp[cnt].init();
dp[cnt].add(,);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
dp[cnt^].init();
if(maped[i][j]==)
dpnone(i,j,cnt,m);
else
dpblank(i,j,cnt,m);
cnt^=;
/* for(int it=1;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
for(int k=0;k<=m;k++)
printf("%d:%d ",k,code[k]);
printf("FLAG:%d fas:%lld\n",endflag,dp[cnt].fas[it]);
}
printf("\n"); */
}
}
for(int i=;i<=dp[cnt].size;i++)
ans+=dp[cnt].fas[i];
return ans;
}
int main()
{
int n,m,kase=;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n,m);
printf("Case %d: %lld\n",++kase,solve(n,m));
}
return ;
}
然后我之前是用独立插头做的,终于过了(78ms):
#include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define LL long long
#define HASH 10007
#define STATE 1000010
using namespace std;
struct hashmap//hash表存状态及大的数和
{
int size;
int next[STATE],head[HASH];
LL state[STATE];
LL fas[STATE];
void init()//清空
{
size=;
clr_1(head);
}
void add(LL st,LL solu)//状态加入,若已有该状态则取较大者
{
int k=st%HASH;
for(int i=head[k];i!=-;i=next[i])
if(st==state[i])
{
if(fas[i]<solu)
fas[i]=solu;
return;
}
next[++size]=head[k];
fas[size]=solu;
state[size]=st;
head[k]=size;
return ;
}
}dp[];
int maped[][];
int code[];
void init(int n,int m)//初始化值,读入maped
{
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
scanf("%d",&maped[i][j]);
}
}
return ;
}
void decode(LL st,int *code,int m)//解码,括号序列
{
for(int i=m;i>=;i--)
{
code[i]=st&;
st>>=;
}
return ;
}
LL encode(int *code,int m)//编码,括号序列
{
LL st=;
for(int i=;i<=m;i++)
{
st<<=;
st|=code[i];
}
return st;
}
void shift(int *code,int m)//左移操作
{
for(int i=m;i>;i--)
code[i]=code[i-];
code[]=;
return ;
}
void dpblank(int i,int j,int cnt,int n,int m)//空格子状态转移
{
int top,left,up;
LL cost;
cost=1LL*maped[i][j];
if(i== &&j==)//i=1 且 j=1时加入只有下独立插头 和 右独立插头的状态
{
decode(dp[cnt].state[],code,m);
if(j+<=m)
{
code[j-]=;
code[j]=;
dp[cnt^].add(encode(code,m),dp[cnt].fas[]+cost);
}
if(i+<=n)
{
code[j-]=;
code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[]+cost);
}
return ;
}
if(i==n &&j==m)//i=n 且 j=m时截止单个独立插头的状态
{
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
code[j-]=code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
return ;
}
for(int it=;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
left=code[j-];
up=code[j];
if(left && up)//上插头和左插头都存在
{
if(left== || up==)//存在一个独立插头
{
if(up==|| left==)//若另一个插头为右括号插头,则找到对应的左括号插头变为独立插头
{
top=;
for(int k=j-;k>=;k--)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
else if(up== ||left==)//若另一个插头为左括号插头,则找到对应的右括号插头变为独立插头
{
top=;
for(int k=j+;k<=m;k++)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
else if(left!=up)//两个括号插头
{
if(left==)//左右括号插头,不允许形成回路
continue;
else//右左括号插头直接去掉
{
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else
{
if(left==)//都是左括号插头,找到对应的第一个右括号插头变为左括号插头
{
top=;
for(int k=j-;k>=;k--)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
else//都是右括号插头,找到对应的第一个左括号插头变为右括号插头
{
top=;
for(int k=j+;k<=m;k++)
{
if(code[k]==)
top++;
if(code[k]==)
if(top>)
top--;
else
{
code[k]=;
break;
}
}
}
code[j]=code[j-]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else if(left || up)//仅有一个插头,则延伸插头
{
if(left) top=left;
else top=up;
if(j+<=m)//右延伸插头
{
code[j-]=;
code[j]=top;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
if(i+<=n)//下延伸插头
{
code[j-]=top;
code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
}
else//没有插头
{
if(j+<=m && i+<=n)//下插头和左插头
{
code[j-]=;
code[j]=;
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]+cost);
}
//可经过可不经过点则可以保持原样,没有插头
code[j-]=code[j]=;
if(j==m) shift(code,m);
dp[cnt^].add(encode(code,m),dp[cnt].fas[it]);
}
}
return ;
}
LL solve(int n,int m)
{
int cnt=;
LL ans=;
if(n== && m==)
return ans=1LL*maped[][];
dp[cnt].init();
dp[cnt].add(,);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
dp[cnt^].init();
dpblank(i,j,cnt,n,m);
cnt^=;
/* for(int it=1;it<=dp[cnt].size;it++)
{
decode(dp[cnt].state[it],code,m);
for(int k=0;k<=m;k++)
printf("%d:%d ",k,code[k]);
printf("fas:%lld\n",dp[cnt].fas[it]);
}
printf("\n"); */
}
}
for(int i=;i<=dp[cnt].size;i++)
ans+=dp[cnt].fas[i];
return ans;
}
int main()
{
int n,m,kase=;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n,m);
printf("Case %d: %lld\n",++kase,solve(n,m));
}
return ;
}
可见增加一维可能导致时间暴增,所以能少递推次数尽量少。
还有测过kuangbin的代码,178ms。状态数过多吧容易变慢。
Black and white
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 254 Accepted Submission(s): 76
Special Judge
You are to calculate how many ways there are to fill the remaining part of the grid under the constraints stated below to make the absolute of (sumBlack - sumWhite) minimum and output one of these ways (if any exist).
Each cell in the grid should be colored either black or white.
All black cells in the grid should be connected with each other, and all white cells should also be connected with each other.The pictures below show two filled grids where this constraint is only fulfilled in the picture2.
There must be no 2x2 blocks in the grid which consists of only white cells, or of only black cells.
The picture3 shows a grid with a black and a white 2x2 block, while the picture4 contains no such 2x2 block.
You are not allowed to change the color of any of the cells whose color has already been assigned in the input, and all cells must be colored.
Each case starts with two integers, N and M (2 ≤ N, M ≤ 8), the number of rows and columns respectively in the grid.
The next N lines contains M characters each and describes the grid using the following characters:
# - a cell which is colored black
o - a cell which is colored white
. - a cell which color has not yet been assigned
The next N lines contains M integers (-1 or 0 or 1) each indicating the number of this gird.
If there are at least one way, then output the minimum absolute of (sumBlack - sumWhite) and the number of ways to fill the grid make it minimum in a line and output one of these ways, using the same format for the grid as in the input.(anyone is ok)
If there is no way to fill the gird under the constraints stated , just output two zero.
Output a blank line after each case.(Special judge.If you not output a blank line after each case, you may get Wrong Answer)
2 3
xxx
oox
1 1 0
0 1 0
2 3
...
...
1 1 0
0 1 -1
5 5
..x..
.....
....o
o....
.x...
1 1 0 0 1
0 -1 -1 0 1
1 1 0 -1 0
1 0 1 -1 -1
-1 -1 1 -1 0
4 5
.....
.....
.....
.....
1 1 0 0 1
0 -1 -1 0 1
1 1 0 -1 0
1 0 1 -1 -1
xxx
oox
Case 2: 0 8
xxx
xox
Case 3: 0 0
Case 4: 1 54
xxxxx
xoxox
oooox
oxxxx
转移状态较多,给出胡浩的题解:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define LL long long
#define HASH 100003
#define STATE 100010
#define INF 0x3f3f3f3f
using namespace std;
char s[][];
int value[][];
int pre[][];
char sta[][];
int code[],ch[];
int n,m;
struct hashmap//hash表存状态及大的数和
{
int size;
int next[STATE],head[HASH];
int state[STATE],dif[STATE],col[STATE];
LL fas[STATE];
void init()//清空
{
size=;
clr_1(head);
}
void add(int st,int difr,int colr,int prer,char chr,LL solu,int id)
{
int k=((st<<)+colr+difr+)%HASH;
for(int i=head[k];i!=-;i=next[i])
if(st==state[i] && difr==dif[i] && colr==col[i])
{
fas[i]+=solu;
return ;
}
state[++size]=st;
dif[size]=difr;
col[size]=colr;
fas[size]=solu;
pre[id][size]=prer;
sta[id][size]=chr;
next[size]=head[k];
head[k]=size;
return ;
}
}dp[];
void init(int n,int m)
{
for(int i=;i<n;i++)
scanf("%s",s[i]);
for(int i=;i<n;i++)
for(int j=;j<m;j++)
scanf("%d",&value[i][j]);
return ;
}
int encode(int *code,int m)
{
int st=;
clr_1(ch);
int ct=-;
for(int i=;i<m;i++)
{
st<<=;
if(ch[code[i]]==-) ch[code[i]]= ++ ct;
st=st|ch[code[i]];
}
return st;
}
void decode(int *code,int m,int st)
{
for(int i=m-;i>=;i--)
{
code[i]=st & ;
st>>=;
}
return ;
}
void print(int k)
{
for(int i=n-;i>=;i--)
for(int j=m-;j>=;j--)
{
s[i][j]=sta[i*m+j][k];
k=pre[i*m+j][k];
}
for(int i=;i<n;i++)
printf("%s\n",s[i]);
return ;
}
void dpblank(int i,int j,int cnt,int c)
{
int lt,up,ltup,col,s1,s2;
for(int it=;it<=dp[cnt].size;it++)
{
decode(code,m,dp[cnt].state[it]);
col=dp[cnt].col[it];
up=(i>)?(col>>j&)==c:;
lt=(j>)?(col>>(j-) & )==c:;
ltup=(i&&j)?(col >> m)==c:;
if(i==n- && j==m- && !lt & !up & ltup) continue;
if(lt && up && ltup) continue;
if(i && !up)
{
s1=s2=;
for(int k=;k<m;k++)
{
if(code[k]==code[j])
s1++;
if((col>>k&)!=c)
s2++;
}
if(s1==)
{
if(s2>) continue;
if(i<n- || j<m-) continue;
}
}
if(lt && up)
{
if(code[j]!=code[j-])
for(int k=,x=code[j];k<m;k++)
if(code[k]==x)
code[k]=code[j-];
}
else if(lt & !up)
code[j]=code[j-];
else if(!lt && !up)
code[j]=m;
if(col & << j) col |= << m;
else col &= ~( << m);
if(c) col |= << j;
else col &= ~( << j);
dp[cnt^].add(encode(code,m),dp[cnt].dif[it]+ (c ? -value[i][j] : value[i][j]),col,it,(c==?'o':'x'),dp[cnt].fas[it],i*m+j);
}
return ;
}
void solve(int n,int m)
{
int cnt=;
dp[cnt].init();
dp[cnt].add(,,,,,,);
for(int i=;i<n;i++)
for(int j=;j<m;j++)
{
dp[cnt^].init();
if(s[i][j]!='x') dpblank(i,j,cnt,);
if(s[i][j]!='o') dpblank(i,j,cnt,);
cnt^=;
};
int minx=INF,ctk;
LL ans=;
int ct;
for(int it=;it<=dp[cnt].size; it++)
{
int s1 = ;
clr(ch);
decode(code,m,dp[cnt].state[it]);
for(int j = ; j < m; j ++) if(!ch[code[j]]) ++s1, ch[code[j]] = ;
if(s1 <= )
{
if(abs(dp[cnt].dif[it]) < minx)
minx=abs(dp[cnt].dif[it]),ans = dp[cnt].fas[it],ctk=it;
else if(abs(dp[cnt].dif[it])==minx)
ans+=dp[cnt].fas[it];
}
}
if(minx==INF) printf("0 0\n");
else
{
printf("%d %lld\n", minx,ans);
print(ctk);
}
return ;
}
int main()
{
int T;
scanf("%d",&T);
for(int kase=;kase<=T;kase++)
{
scanf("%d%d",&n,&m);
init(n,m);
printf("Case %d: ",kase);
solve(n,m);
printf("\n");
}
return ;
}
插头dp
插头dp练习的更多相关文章
- 插头dp
插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...
- HDU 4113 Construct the Great Wall(插头dp)
好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...
- HDU 4949 Light(插头dp、位运算)
比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...
- 插头DP专题
建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...
- HDU 1693 Eat the Trees(插头DP)
题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...
- 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 ...
- URAL 1519 基础插头DP
题目大意: 给定一个图,一部分点'*'作为障碍物,求经过所有非障碍点的汉密尔顿回路有多少条 基础的插头DP题目,对于陈丹琦的论文来说我觉得http://blog.sina.com.cn/s/blog_ ...
- uva 11270 - Tiling Dominoes(插头dp)
题目链接:uva 11270 - Tiling Dominoes 题目大意:用1∗2木块将给出的n∗m大小的矩阵填满的方法总数. 解题思路:插头dp的裸题,dp[i][s]表示第i块位置.而且该位置相 ...
- bzoj 1187: [HNOI2007]神奇游乐园 插头dp
1187: [HNOI2007]神奇游乐园 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 668 Solved: 337[Submit][Statu ...
随机推荐
- 用 Docker 来构建 Jumpserver
说明: 项目从 [ Jumpserver 官方 ] fork 而来. 主要更新: OS: ubuntu:18.04 优化了 Dockerfile Jumpserver 版本: 1.4.0 redis ...
- 【POJ3254】coinfield
状压dp初步. #include<iostream> #include<cstdio> #include<cstring> #include<algorith ...
- 【bzoj4033】HAOI2015树上染色
树形dp. #include<bits/stdc++.h> #define N 2010 using namespace std; typedef long long ll; ,head[ ...
- Winfrom窗体间传值
1.通过tag属性传输,tag属性是存储与空间密切相关的数据.比如登陆界面的数据传输给主界面. 子窗体 ...
- make :err Makefile.ssl is older than Makefile.org. Reconfigure the source tree (via './config' or 'perl Configure'), please.
内核编译时出现错误 Makefile.ssl is older than Makefile.org. Reconfigure the source tree (via './config' or 'p ...
- 关于那些Android中不常用的设置属性
很多在manifest中的属性我们经常遗忘了它们,或者经常看到但又不是很明白它的作用.那么在这里我就拿了一些属性简单的解释一下,防止以后碰到却不知道其中的意思.不是很全,以后会断断续续的补充吧 一.a ...
- XML、java解释XML、XML约束
1.XML有什么用? (1)可以用来保存数据 (2)可以用来做配置文件 (3)数据传输载体 2.XML格式 XML 元素必须遵循以下命名规则: 名称可以含字母.数字以及其他的字符 名称不能以数字或者标 ...
- 883H - Palindromic Cut(思维+STL)
题目链接:http://codeforces.com/problemset/problem/883/H 题目大意:给一段长度为n的字符串s,想让你把s切成几段长度相同的回文串,可以改变s中字符的排列, ...
- WP的SEO工具汇总
Baidu Sitemap Generator 百度站点地图生成工具 https://wordpress.org/plugins/baidu-sitemap-generator/ This pulg ...
- [你必须知道的.NET]第二十五回:认识元数据和IL(中)
发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...