插头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 ...
随机推荐
- [原创]JavaEE在CentOS服务器上的部署
1.安装rz.sz命令 yum install lrzsz 2.配置环境变量 vi .bash_profile 默认的path:PATH=$PATH:$HOME/.local/bin:$HOME/bi ...
- 实现放大转场动画 from cocoachina
原文1:http://www.cocoachina.com/ios/20160318/15714.html 原文2:http://ningandjiao.iteye.com/blog/2049105 ...
- win10 安装framework3.5
win+x 点击命令提示符(个管理员) 输入dism.exe /online /enable-feature /featurename:NetFX3 /Source:H:\sources\sxs(解压 ...
- SQL(横表和纵表)行列转换,PIVOT与UNPIVOT的区别和使用方法举例,合并列的例子
使用过SQL Server 2000的人都知道,要想实现行列转换,必须综合利用聚合函数和动态SQL,具体实现起来需要一定的技巧,而在SQL Server 2005中,使用新引进的关键字PIVOT/UN ...
- marquee实现文字移动效果;js+div实现文字无缝移动效果
1.marquee实现文字移动: <marquee width="220px;" scrollamount="5" onmouseover="t ...
- iOS 之UIButton左文右图
对于button,当添加了图片时,默认是左图右文的 '[self.pageViewsLB setImage:[UIImage imageNamed:@"read"] forStat ...
- MySQLFabric连接的编码问题
今天解决的一个小问题.最终的解决方案很简单,主要是讲一下解决问题的思路. 测试人员在服务器上测试,页面提交的中文内容存入数据库中以后,是乱码. 开发人员在本机上测试,没有问题. 服务器上使用的是Mys ...
- NOPI读取模板导出(Excel中追加数据)
在Controller里,我们定义一个FileResult的Action,返回值是一个文件形式被浏览器下载下来. [HttpGet] public FileResult ExportProductLi ...
- sqlserver中BCP命令导入导出
个人自用导出文本文件命令: bcp [xxDB].[dbo].[xx_tb_name] out d:\temp\xxx.txt -c -t "\t" -T bcp是SQL Serv ...
- 用python实现一个不排序的列表功能
#!/usr/bin/env python # -*- coding: utf-8 -*- # learn <<Problem Solving with Algorithms and Da ...