1. 程式人生 > 其它 >插頭DP 學習筆記

插頭DP 學習筆記

(供複習使用)
插頭,其實就是一條線的端點
特別的,左邊對應左插頭,右邊對應右插頭
插頭DP其實就是一類狀壓
\(1\) 代表該位置為左插頭
\(2\) 代表為右插頭
\(0\) 表示無插頭
圖就不畫了,很多題解都畫得很好,
這裡只解釋一下轉移
(此時列舉的狀態為 zt 方案數為 val b1 為臺階的側邊 b2 為臺階的上端)

if(!mp[i][j]) { if(!b1&&!b2) { ins(zt,val); } }

如果該點不能放點,左上為零才合法

if(!b1&&!b2) { if(mp[i+1][j]&&mp[i][j+1]) { ins(zt+bin[j-1]+2*bin[j],val); } }

如果左上為零,那只有聯通右下才合法

f(!b1&&b2) {
    if(mp[i][j+1]) ins(zt,val);
    if(mp[i+1][j]) ins(zt+b2*bin[j-1]-b2*bin[j],val);
} 
if(b1&&!b2) {
    if(mp[i][j+1]) ins(zt+b1*bin[j]-b1*bin[j-1],val);
    if(mp[i+1][j]) ins(zt,val);
}

如果只有左零或上零,考慮另一端端的連線方向即可

if(b1==1&&b2==1) {
    int kl=1;
    for(re int t=j+1;t<=m;t++) {
	if((zt>>(t*2))%4==1) kl++;
	if((zt>>(t*2))%4==2) kl--;
	if(!kl) { ins(zt-bin[j]-bin[j-1]-bin[t],val); break; }
    }
}
if(b1==2&&b2==2) {
    int kl=1;
    for(re int t=j-2;t>=0;t--) {
	if((zt>>(t*2))%4==1) kl--;
	if((zt>>(t*2))%4==2) kl++;
	if(!kl) { ins(zt-2*bin[j]-2*bin[j-1]+bin[t],val); break; }
    }
}

如果左上相等,連線變值即可

if(b1==2&&b2==1) ins(zt-2*bin[j-1]-bin[j],val);
if(i==e1&&j==e2) ans+=val;

兩種連值,其中 \(e1\)\(e2\) 為最後列舉到的可放點