日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

插头DP小结_dp插头接线标准

發布時間:2023/12/19 45 生活家
生活随笔 收集整理的這篇文章主要介紹了 插头DP小结_dp插头接线标准 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

插頭DP一般都是棋盤模型,找路徑或者環路最值或者方案數。
插頭:說白了就是兩個聯通的格子,一個走向另一個,那么這里就有一個插頭。
輪廓線:DP逐格DP,那么輪廓線可以分開DP過的格子和未DP的格子。輪廓線的長度明顯是m+1。插頭垂直于輪廓線。
轉移:
輪廓線在換行的時候要位移,這個畫畫圖就出來了。
然后具體問題具體討論。比如任意多個環路,不考慮方向,那么就是eat the trees,用最小表示法,因為是任意多個環路,那么插頭只有兩種,一種是有插頭,一種是沒插頭,具體聯通與否我們不管。如果要考慮方向呢?那么插頭就有3種,一種是沒插頭,一種是插頭從已DP的指向未DP的,一種是未DP的指向已DP的。
具體實現,有兩種思路,一種是括號序列,一種是最小表示法。
括號序列比較快,空間壓縮得很好,不過轉移太麻煩辣。
最小表示法轉移比較好想,就是比較慢,空間比較大。
寫法有三種,一種是hash表存取狀態,有decode,encode,就是kuangbin那種寫法;一種是傳統dp寫法,位運算取出狀態;還有種是claris寫法,預處理所有可能狀態然后傳統DP轉移。
kuangbin那個因為位運算比較少,每次都會直接接觸到解密的狀態,比較直觀好想,模式化很強,不過每次都有O(m)的常數用在加密解密上。時空耗費較大,要寫hash表,代碼較長。
傳統DP轉移有的是O(1),有的O(n),總體來說和上面的差不多。。因為遞推轉移無效狀態比較多。然后代碼比較短。缺點就是一堆位運算像我這種傻逼根本看不懂
claris寫法太神辣。因為所有狀態預處理好了所以狀態數很少,因為預處理所以所有轉移O(1),然后代碼很短。缺點是我這種傻逼不會預處理。然后還是一堆位運算。并且遇到題目本身狀態很多的時候效果不會很好。
我現在只會第一種寫法。
下面扔2個例題。
HYSBZ 3125
找一條走過所有格子的環路的方案數。
有的格子只能上下經過,有的只能左右經過,有的不能經過。
這個題我寫的括號序列。
插頭3種,空插頭,左括號,右括號。
然后分9類情況討論即可。
因為分了9類情況所以代碼長爆。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int HASH=10007,STATE=1000010,MAXD=15;
int n,m,ex,ey,code[MAXD];
char maze[MAXD][MAXD];
struct HASHMAP{
int head[HASH],next[STATE],state[STATE],size;
ll f[STATE];
void init()
{
size=0;
memset(head,-1,sizeof head);
}
void push(int st,ll val)
{
int h=st%HASH;
for(int i=head[h];i!=-1;i=next[i])
if(st==state[i])
{
f[i]+=val;
return;
}
f[size]=val;
state[size]=st;
next[size]=head[h];
head[h]=size++;
}
}hm[2];
int encode(int *code,int m)
{
int st=0;
for(int i=0;i<=m;i++)
{
st*=3;
st+=code[i];
}
return st;
}
void decode(int *code,int m,int st)
{
for(int i=m;i>=0;i--)
{
code[i]=st%3;
st/=3;
}
}
void shift(int *code,int m)
{
for(int i=m;i>0;--i)code[i]=code[i-1];
code[0]=0;
}
void dpblank(int i,int j,int cur)
{
for(int k=0;k<hm[cur].size;++k)
{
decode(code,m,hm[cur].state[k]);
int &p=code[j-1],&q=code[j];
if(!p&&!q)
{
if((maze[i+1][j]=='|'||maze[i+1][j]=='.')&&(maze[i][j+1]=='-'||maze[i][j+1]=='.'))
{
p=1,q=2;
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
else if(p&&q)
{
if(p==1&&q==1)
{
int tot=0;
for(int ii=j;ii<=m;++ii)
{
if(code[ii]==1)++tot;
if(code[ii]==2)--tot;
if(tot==0)
{
code[ii]=1;
break;
}
}
p=0,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
else if(p==2&&q==2)
{
int tot=0;
for(int ii=j-1;ii>=0;--ii)
{
if(code[ii]==2)++tot;
if(code[ii]==1)--tot;
if(tot==0)
{
code[ii]=2;
break;
}
}
p=0,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
else if(p==1&&q==2)
{
if(i==ex&&j==ey)
{
p=0,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
else if(p==2&&q==1)
{
p=0,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
else
{
if(maze[i][j+1]=='.'||maze[i][j+1]=='-')
{
q=p+q,p=0;
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
if(maze[i+1][j]=='.'||maze[i+1][j]=='|')
{
p=p+q,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
}
}
void dpver(int i,int j,int cur)
{
for(int k=0;k<hm[cur].size;++k)
{
decode(code,m,hm[cur].state[k]);
int &p=code[j-1],&q=code[j];
if(p==0&&q)
{
if(maze[i+1][j]=='.'||maze[i+1][j]=='|')
{
p=q,q=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
}
}
void dphor(int i,int j,int cur)
{
for(int k=0;k<hm[cur].size;++k)
{
decode(code,m,hm[cur].state[k]);
int &p=code[j-1],&q=code[j];
if(p&&q==0)
{
if(maze[i][j+1]=='.'||maze[i][j+1]=='-')
{
q=p,p=0;
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
}
}
void dpblock(int i,int j,int cur)
{
for(int k=0;k<hm[cur].size;++k)
{
decode(code,m,hm[cur].state[k]);
code[j-1]=0,code[j]=0;
if(j==m)shift(code,m);
hm[cur^1].push(encode(code,m),hm[cur].f[k]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%s",maze[i]+1);
for(int j=1;j<=m;++j)
if(maze[i][j]=='.')ex=i,ey=j;
}
int cur=0;
ll ans=0;
hm[cur].init();
hm[cur].push(0,1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
hm[cur^1].init();
if(maze[i][j]=='.')dpblank(i,j,cur);
else if(maze[i][j]=='|')dpver(i,j,cur);
else if(maze[i][j]=='-')dphor(i,j,cur);
else dpblock(i,j,cur);
cur^=1;
}
for(int i=0;i<hm[cur].size;++i)ans+=hm[cur].f[i];
printf("%lld\n",ans);
}

Jetbrains全家桶1年46,售后保障穩定

bzoj2310 parkii
這個題就是弱化的zoj3213
棋盤上的格子有權值。找一條路徑使路徑上的權值之和最大。
這題我用的最小表示法,需要因為這個是路徑不是回路,需要增加獨立插頭,同事需要增加標志位記錄獨立插頭個數。要使獨立插頭個數小于等于2.具體實現的時候把標志位也壓進狀態里面。
最小表示法就相對好討論一些。因為括號序列的性質,輪廓線上m+1個點最多只有m/2個不同的聯通塊,根據這個壓位DP。
如果左插頭和上插頭都有,那么右插頭和下插頭就沒有了。有以下2種情況:
1、如果左插頭和上插頭都有且不在一個聯通塊內,就連接他們。連接還要重標號最小表示。因為以前左插頭所在聯通塊和上插頭所在聯通塊不是一個,現在要暴力修改。
2、如果左插頭和上插頭都有且在一個聯通塊內,就不管了。
如果左插頭和上插頭只有一個,那么就有以下3種情況:
1、這個插頭可以延續一個右插頭
2、這個插頭可以延續一個下插頭
3、這個插頭可以延續一個獨立插頭
注意延續一個右/下插頭不要走出棋盤了,注意延續獨立插頭的前提是輪廓線上獨立插頭數量小于2
如果左插頭和上插頭都沒有,那么就有以下3種情況:
1、右插頭和下插頭都得有,現在他們倆在一個聯通塊內。注意這樣不要走出棋盤了。
2、左插頭變成獨立插頭再延續一個右插頭。注意延續獨立插頭的前提是輪廓線上獨立插頭數量小于2。
3、右插頭變成獨立插頭再延續一個左插頭。注意延續獨立插頭的前提是輪廓線上獨立插頭數量小于2。
這樣就討論完了。注意換行的時候狀態要位移。如果用括號序列分類有16種= =這個我太傻逼了不會做。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int HASH=10007,STATE=1000010,MAXD=15;
int n,m,num,code[MAXD],ch[MAXD],maze[105][MAXD];
struct HASHMAP{
int head[HASH],next[STATE],state[STATE],f[STATE],size;
void init()
{
size=0;
memset(head,-1,sizeof head);
}
void push(int st,int val)
{
int h=st%HASH;
for(int i=head[h];~i;i=next[i])
if(st==state[i])
{
f[i]=max(f[i],val);
return;
}
f[size]=val;
state[size]=st;
next[size]=head[h];
head[h]=size++;
}
}hm[2];
int encode(int *code,int m)
{
int st=0,cnt=1;
memset(ch,-1,sizeof ch);
ch[0]=0;
for(int i=0;i<=m;++i)
{
if(ch
]==-1)ch
]=cnt++; code[i]=ch
]; st<<=3; st|=code[i]; } st<<=3; st|=num; return st; } void decode(int *code,int m,int st) { num=st&7; st>>=3; for(int i=m;i>=0;--i) { code[i]=st&7; st>>=3; } } void dp(int i,int j,int cur) { for(int k=0;k<hm[cur].size;++k) { decode(code,m,hm[cur].state[k]); int p=code[j-1],q=code[j]; if(p&&q) { if(p!=q) { for(int ii=0;ii<=m;++ii) if(code[ii]==q)code[ii]=p; code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==m?m-1:m),hm[cur].f[k]+maze[i][j]); } } else if(p||q) { if(j+1<=m) { code[j]=p+q,code[j-1]=0; hm[cur^1].push(encode(code,m),hm[cur].f[k]+maze[i][j]); } if(i+1<=n) { code[j-1]=p+q,code[j]=0; hm[cur^1].push(encode(code,j==m?m-1:m),hm[cur].f[k]+maze[i][j]); } if(num<2) { ++num; code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==m?m-1:m),hm[cur].f[k]+maze[i][j]); } } else { code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==m?m-1:m),hm[cur].f[k]); if(j+1<=m&&i+1<=n) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,m),hm[cur].f[k]+maze[i][j]); } if(num<2) { ++num; if(j+1<=m) { code[j-1]=0,code[j]=13; hm[cur^1].push(encode(code,m),hm[cur].f[k]+maze[i][j]); } if(i+1<=n) { code[j-1]=13,code[j]=0; hm[cur^1].push(encode(code,j==m?m-1:m),hm[cur].f[k]+maze[i][j]); } } } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&maze[i][j]); int cur=0,ans=-999999999; hm[cur].init(); hm[cur].push(0,0); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { ans=max(ans,maze[i][j]); hm[cur^1].init(); dp(i,j,cur); cur^=1; } for(int i=0;i<hm[cur].size;++i)ans=max(ans,hm[cur].f[i]); printf("%d\n",ans); }

總結

以上是生活随笔為你收集整理的插头DP小结_dp插头接线标准的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。