插头dp
插头dp
感受:
我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案。而是方案本来就在那里,我们只是枚举状态统计了答案。
看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单
就像lyd说的,考插头dp的题目就是在考模板2333
(学这个之前连hash_map都没写过2333
WA:
(1) 初始化矩阵,周围格子有可能是0--->转移出错
(2)统计答案最后统计的是合法的,即st==0的。。。
题目集锦:
(1)cojs1512 经过所有可经过的点的一条回路个数
因为是一条回路,依次dp每个点的状态,所以记录endx,endy只在终点更新答案,其它点的闭合回路不计算。
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define mod 13131
#define N 4500
#define ll long long
struct dp_hash{
int head[mod],next[N],sz;
ll f[N],st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]+=ins;return;
}
sz++;
f[sz]=ins;st[sz]=S;
next[sz]=head[now];
head[now]=sz;
}
}dp[2];
ll ans=0;
int n,m,code[16],ch[16],a[16][16],cur,Endx,Endy;
char s[15];
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
ll Encode(){
ll S=0;
memset(ch,-1,sizeof(ch));int cnt=0;
ch[0]=0;
for(int i=0;i<=m;i++){
if(ch[code[i]]==-1)ch[code[i]]=++cnt;
code[i]=ch[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Shift(){
for(int i=m;i>0;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,bool type){
if(!type){
for(int k=1;k<=dp[pre].sz;k++){
Decode(dp[pre].st[k]);
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
return;
}
for(int k=1;k<=dp[pre].sz;k++){
Decode(dp[pre].st[k]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
if(Left==Up){
if(Endx==x&&Endy==y)
ans+=dp[cur].f[k];
}
else{
code[y]=code[y-1]=0;
for(int i=0;i<=m;i++)
if(code[i]==Up)code[i]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
else if(Left==0&&Up==0){
if(a[x][y+1]&&a[x+1][y]){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]){
code[y-1]=0;code[y]=tmp;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
if(a[x+1][y]){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
}
}
int main(){
freopen("formula1.in","r",stdin);
freopen("formula1.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]!='*'){
a[i][j]=1;
Endx=i;Endy=j;
}
} if(Endx==0){
puts("0");return 0;
}
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
printf("%lld\n",ans);
return 0;
}
(2) hdu1693 Eat The Trees
经过所有非障碍点的回路个数(不限条数)。
和上一道题的区别就是非终点的回路也要更新其它状态。
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define N 1000000
#define mod 13131
#define ll long long
struct dphash{
int head[N],next[N],sz;
ll st[N],f[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]+=ins;return;
}
sz++;
st[sz]=S;f[sz]=ins;
next[sz]=head[now];head[now]=sz;
}
}dp[2];
int n,m,T,cur,pw[16],a[16][16],code[16];
ll ans;
ll Encode(){
ll S=0;
memset(pw,-1,sizeof(pw));
pw[0]=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,bool type){
if(!type){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);
// cout<<"code "<<code[y]<<' '<<code[y-1]<<endl;
code[y]=code[y-1]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
return;
}
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
code[y]=code[y-1]=0;
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
else if(Left==0&&Up==0){
if(a[x][y+1]&&a[x+1][y]){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]){
code[y]=tmp;code[y-1]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
if(a[x+1][y]){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
}
}
int main(){
// freopen("1693.in","r",stdin);
// freopen("1693.out","w",stdout);
T=read();
for(int k=1;k<=T;k++){
memset(a,0,sizeof(a));
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)a[i][j]=read();
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
ans=0;
for(int i=dp[cur].head[0];i;i=dp[cur].next[i])if(dp[cur].st[i]==0)ans+=dp[cur].f[i];
printf("Case %d: There are %I64d ways to eat the trees.\n",k,ans);
}
return 0;
}
(3)[国家集训队2011]画圈圈
根据射线法,判断一个点左边的下插头奇偶性判断是否在回路内。
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define ll long long
#define N 1000000
#define mod 13131
#define MOD 123456791
struct dp_hash{
int head[mod],next[N],sz;
ll f[N],st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
ins%=MOD;int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
(f[i]+=ins)%=MOD;return;
}
sz++;next[sz]=head[now];head[now]=sz;
st[sz]=S;f[sz]=ins;
}
}dp[2];
int n,m,cur,pw[16],a[25][16],code[16];
ll ans;
char s[16];
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
ll Encode(){
ll S=0;
memset(pw,-1,sizeof(pw));
pw[0]=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,int type){
if(type){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);int t=0;
for(int j=0;j<y-1;j++)if(code[j]!=0)t++;
if((t%2==1&&type==1)||(t%2==0&&type==2)){
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
return;
} for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
code[y]=code[y-1]=0;
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
else if(Left==0&&Up==0){
if(a[x][y+1]==0&&a[x+1][y]==0){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]==0){
code[y]=tmp;code[y-1]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
if(a[x+1][y]==0){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
}
}
int main(){
freopen("nt2011_circle.in","r",stdin);
freopen("nt2011_circle.out","w",stdout);
n=read();m=read();
memset(a,-1,sizeof(a));
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='.')a[i][j]=0;
else if(s[j]=='*')a[i][j]=1;
else a[i][j]=2;
}
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
for(int i=dp[cur].head[0];i;i=dp[cur].next[i])if(dp[cur].st[i]==0){ans=dp[cur].f[i];break;}
printf("%lld\n",ans);
}
(4)BZOJ1187
都是把记录方案个数的变量改为记录当前状态得到的权值
注意的是每个状态现在要记录的是最大值,就是说一个状态可以对应多种可能,你只有一个轮廓线,但轮廓线上方是怎么样的无法确定,所以要取ma]x
还有就是更新答案的时候,要code[y]=code[y-1]=0,然后判断Encode()==0,否则的话有可能两边还有插头,这样下面还会更新一次。
边界什么的也比较麻烦,容易写漏。
(5) BZOJ3753
这道我感觉超难的qaq,注意dp的是矩阵的边,但权值还是格子。。然后就会发现不会转移,奥妙重重。。
smz原来教过我,然而我忘了QAQ
然后她现在人在衡水QAQ不上QQ QAQ
问了大爷。。还没回复QAQ
还没写QAQ
update 前几天终于写了,这个其实可以理解为是否放置了下插头
首先就是n++,m++,然后选择一条边(即新图的一个格子),如果它有下插头,才能对当前格子是否在圈内产生影响。
贴代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
#define mod 13131
#define N 1000000
#define inf 1e9
int n,m,code[20],a[15][15],v[15][15],num=0,ans=-inf;
struct hash_map{
int head[mod],next[N],sz,f[N];
ll st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,int ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]=max(f[i],ins);return;
}
st[++sz]=S;f[sz]=ins;
next[sz]=head[now];head[now]=sz;
}
}dp[2];
int pw[20];
ll Encode(){
memset(pw,-1,sizeof(pw));
pw[0]=0;ll S=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S=S<<3|code[i];
}
return S;
}
void Decode(ll S){
for(int i=m;i>=0;i--)
code[i]=S&7,S>>=3;
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
bool check(int x,int y,bool is){
if(a[x][y]==1&&is)return 0;
if(a[x][y]==2&&is==0)return 0;
return 1;
}
void DP(int x,int y,int pre){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);
if(y==1){if(code[m])continue;Shift();}
int Left=code[y-1],Up=code[y];
bool is=0;
for(int j=0;j<y-1;j++)if(code[j])is^=1;
if(Left&&Up){
code[y]=code[y-1]=0;
if(Left==Up){
if(Encode()==0&&num==0)ans=max(ans,dp[pre].f[i]);
}
else{
if(check(x,y,is)){
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
}
else if(Left||Up){
int tmp=Left?Left:Up;
if(y!=m&&check(x,y,is)){
code[y-1]=0;code[y]=tmp;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
is^=1;
if(x!=n&&check(x,y,is)){
code[y-1]=tmp;code[y]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
else{
if(check(x,y,is))dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
is^=1;
if(x!=n&&y!=m&&check(x,y,is)){
code[y]=code[y-1]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
}
}
int main(){
// freopen("test.in","r",stdin);
n=read();m=read();n++;m++;
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)v[i][j]=read();
int cur=0;dp[cur].init();dp[cur].push(0,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur);
cur^=1;
}
printf("%d\n",ans);
for(int i=1;i<n;i++)
for(int j=1;j<m;j++){
a[i][j]=read();
if(a[i][j]==2)num++;
}
cur=0;ans=-inf;
dp[cur].init();dp[cur].push(0,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur);
cur^=1;
if(a[i][j]==2)num--;
}
if(ans==-inf)printf("Can not establish GFW.");
else printf("%d\n",ans);
return 0;
}
插头dp的更多相关文章
- 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 ...
随机推荐
- ORA-01502: index 'INDEX_NAME' or partition of such index is in unusable state
ORA-01502: index 'INDEX_NAME' or partition of such index is in unusable state 原因: 这个错误一般是因为索引状态为UNUS ...
- Python 30分钟入门——数据类型 and 控制结构
Python是一门脚本语言,我也久闻大名,但正真系统的接触学习是在去年(2013)年底到今年(2014)年初的时候.不得不说的是Python的官方文档相当齐全,如果你是在Windows上学习Pytho ...
- struts2download
public class TestAction extends ActionSupport{ public String fileLoad() throws IOException{ ...
- tornado 异步调用系统命令和非阻塞线程池
项目中异步调用 ping 和 nmap 实现对目标 ip 和所在网关的探测 Subprocess.STREAM 不用担心进程返回数据过大造成的死锁, Subprocess.PIPE 会有这个问题. i ...
- [Sass]嵌套
[Sass]嵌套-选择器嵌套 Sass 中还提供了选择器嵌套功能,但这也并不意味着你在 Sass 中的嵌套是无节制的,因为你嵌套的层级越深,编译出来的 CSS 代码的选择器层级将越深,这往往是大家不愿 ...
- webservices接口 file "/axis2-web/listsingleservice.jsp" not found 问题解决
搞了半天 ,原来是services.xml 配置的某个或者某些service 在代码中不存才.扫描的时候找不到对应的service代码所以就会报错
- Css样式表【边界边框】【列表方块】
一.如何给div加边框?[边界边框] 我们先做一个整个边框,并附加成绿色的边框. ①使用属性border进行设置. →→ ②也可以单独更改某个边的边框线的颜色,利用border属性更改. 如果将div ...
- android studio使用ndk,jni随记
1.下载ndk(会下载到sdk目录下) 2.配置 3.配置build.gradle(app) android { defaultConfig { ndk { moduleName "JniD ...
- 如何让include标签包裹的布局置于屏幕最下方?
如何让一个Layout 始终在屏幕的下方 我想让<include layout="@layout/bottom" />一直在屏幕下,怎么做? 1.相对布局中用属性 a ...
- Datazen地图Chart自定义数据
此篇介绍如何将数据关联到Datazen地图图表.我们会将数据库中的数据映射到地图上. 首先查看下默认地图图表绑定的数据.以下是系统自带的美国地图数据,主要有两列,一列为地名,一列为度量数据.地图会根据 ...