[专题总结]初探插头dp

彻彻底底写到自闭的一个专题。

就是大型分类讨论,压行+宏定义很有优势。

常用滚动数组+哈希表+位运算。当然还有轮廓线。

 

Formula 1:

经过所有格子的哈密顿回路数。

每个非障碍点必须有且仅有2个插头(含上下左右)。

若左上都没有,那么新建两个插头1和2。

若左上只有一个插头,那么它就向右下方向之一延伸。

若左上都有,那么不建新插头。

如果是左1上2,那么就是形成了一条回路,当且仅当在全图右下角更新答案。

如果都是1,那么就要把右边的两个2改成一对插头,就是把靠右的一个1插头匹配的2插头改为1。

都是2同理。

如果是左2上1,那么就是链接了两个路径,两个插头抵消。

 1 #include
 2 #define cri const register int
 3 int bl[13][13],n,m,now,last=1,ln,lm,fn,fm,r[3]={0,1,-1};long long ans;
 4 int read(){
 5     register char ch=getchar();
 6     while(ch!='*'&&ch!='.')ch=getchar();
 7     return ch=='*';
 8 }
 9 struct hash{
10     int fir[54321],l[54321],to[54321],cnt;long long v[54321];
11     void clear(){
12         for(int i=0;i<54321;++i)to[i]=-1,fir[i]=v[i]=0;cnt=0;
13     }
14     long long &operator[](cri pos){
15         int i;
16         for(i=fir[pos%54321];i&&to[i]!=pos;i=l[i]);
17         if(!i)l[++cnt]=fir[pos%54321],to[cnt]=pos,fir[pos%54321]=i=cnt;
18         return v[i];
19     }
20 }f[2];
21 inline int find(cri st,cri p){return st>>(p-1<<1)&3;}
22 inline void set(int &st,cri p,cri k){st&=~(3<<(p-1<<1));st|=k<<(p-1<<1);}
23 inline int get(cri st,cri p){
24     int dirc=(find(st,p)==1)?1:-1;
25     for(int i=p,pl=find(st,i),cnt=r[pl];i&&i<=m+1;pl=find(st,i+=dirc),cnt+=r[pl])
26         if(cnt==0)return i;
27 }
28 int main(){
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
31         bl[i][j]=read();
32         if(!bl[i][j])ln=i,lm=j;
33         if(!fn&&!bl[i][j])fn=i,fm=j;
34     }f[0][0]=1;
35     for(int i=1;i<=n;++i)bl[i][m+1]=1;
36     for(int i=1;i<=m;++i)bl[n+1][i]=1;
37     for(int i=fn;i<=n;++i){
38         for(int j=(i==fn)?fm:1;j<=m;++j){
39             last^=1;now^=1; f[now].clear();
40             for(int k=1;k<=f[last].cnt;++k){
41                 int st=f[last].to[k],p1=find(st,j),p2=find(st,j+1);
42                 long long v=f[last].v[k];//printf("%d %d %d %d %d %lld\n",i,j,st,p1,p2,v);
43                 if(bl[i][j]&&(p1||p2))continue;
44                 if(bl[i][j])f[now][st]+=v;
45                 else if(!p1&&!p2){if(!bl[i][j+1]&&!bl[i+1][j])set(st,j,1),set(st,j+1,2),f[now][st]+=v;}
46                 else if(p1&&!p2){
47                     if(!bl[i+1][j])f[now][st]+=v;//,printf("---%d %d %lld %lld\n",i,j,v,f[now][st]);
48                     if(!bl[i][j+1])set(st,j+1,p1),set(st,j,0),f[now][st]+=v;
49                 }
50                 else if(!p1&&p2){
51                     if(!bl[i][j+1])f[now][st]+=v;
52                     if(!bl[i+1][j])set(st,j,p2),set(st,j+1,0),f[now][st]+=v;
53                 }
54                 else if(p1==1&&p2==2){if(i==ln&&j==lm)ans+=v;}
55                 else if(p2==1&&p1==2)set(st,j,0),set(st,j+1,0),f[now][st]+=v;
56                 else if(p1==1&&p2==1)set(st,get(st,j+1),1),set(st,j,0),set(st,j+1,0),f[now][st]+=v;
57                 else if(p1==2&&p2==2)set(st,get(st,j),2),set(st,j,0),set(st,j+1,0),f[now][st]+=v;
58             }
59         }
60         for(int i=1;i<=f[now].cnt;++i)f[now].to[i]<<=2;
61     }
62     printf("%lld\n",ans);
63 }
当时我还不会压行

 

CITY

和上面同理,只不过限制了插头方向。

 1 #include
 2 #define int long long
 3 #define mod 54321
 4 int n,m,bl[15][15],ref[129],fn,fm,ln,lm,last=1,now,tf[3]={0,1,-1};long long ans;
 5 int read(){
 6     register char ch=getchar();
 7     while(ch!='.'&&ch!='#'&&ch!='-'&&ch!='|')ch=getchar();
 8     return ref[ch];
 9 }
10 struct hash{
11     int fir[mod],l[mod],to[mod],cnt;long long v[mod];
12     void clear(){for(int i=0;i0,to[i]=-1;cnt=0;}
13     long long &operator[](int p){
14         int i;
15         for(i=fir[p%mod];i&&to[i]!=p;i=l[i]);
16         if(!i)l[++cnt]=fir[p%mod],fir[p%mod]=i=cnt,to[cnt]=p;
17         return v[i];
18     }
19 }f[2];
20 int find(int st,int p){return st>>(p-1<<1)&3;}
21 void set(int &st,int p,int k){st&=~(3<<(p-1<<1));st|=k<<(p-1<<1);}
22 void reset(int &st,int p){
23     for(int i=p,pl=find(st,i),di=tf[find(st,p)],cnt=di;i&&i<=m+1;i+=di,pl=find(st,i),cnt+=tf[pl])
24         if(!cnt){set(st,i,pl==1?2:1);return;}
25 }
26 main(){
27     scanf("%lld%lld",&n,&m);ref['#']=3;ref['-']=1;ref['|']=2;
28     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
29         bl[i][j]=read();
30         if(!bl[i][j])ln=i,lm=j;
31         if(!bl[i][j]&&!fn)fn=i,fm=j;
32     }
33     f[0][0]=1;
34     for(int i=1;i<=n;++i)bl[i][m+1]=3;
35     for(int i=1;i<=m;++i)bl[n+1][i]=3;
36     for(int i=fn;i<=n;++i){
37         for(int j=(i==fn?fm:1);j<=m;++j){
38             last^=1;now^=1;f[now].clear();
39             for(int k=1;k<=f[last].cnt;++k){
40                 int st=f[last].to[k],p1=find(st,j),p2=find(st,j+1);long long v=f[last].v[k];//printf("%d %d %d %d %d %lld\n",i,j,st,p1,p2,v);
41                 if(((bl[i][j]&1)&&p2)||((bl[i][j]&2)&&p1))continue;
42                 if(bl[i][j]==3){if(!p1&&!p2)f[now][st]+=v;continue;}
43                 if(bl[i][j]==2){if((!(bl[i+1][j]&1))&&p2&&!p1)set(st,j+1,0),set(st,j,p2),f[now][st]+=v;continue;}
44                 if(bl[i][j]==1){if((!(bl[i][j+1]&2))&&p1&&!p2)set(st,j,0),set(st,j+1,p1),f[now][st]+=v;continue;}
45                 if(!p1&&!p2){if(!(bl[i][j+1]&2)&&!(bl[i+1][j]&1))set(st,j,1),set(st,j+1,2),f[now][st]+=v;}
46                 else if(p1&&!p2){
47                     if(!(bl[i+1][j]&1))f[now][st]+=v;
48                     if(!(bl[i][j+1]&2))set(st,j,0),set(st,j+1,p1),f[now][st]+=v;
49                 }
50                 else if(p2&&!p1){
51                     if(!(bl[i][j+1]&2))f[now][st]+=v;
52                     if(!(bl[i+1][j]&1))set(st,j+1,0),set(st,j,p2),f[now][st]+=v;
53                 }
54                 else if(p1==1&&p2==2){if(i==ln&&j==lm)ans+=v;}
55                 else if(p2==1&&p1==2)set(st,j,0),set(st,j+1,0),f[now][st]+=v;
56                 else if(p1==1&&p2==1)reset(st,j+1),set(st,j,0),set(st,j+1,0),f[now][st]+=v;
57                 else if(p1==2&&p2==2)reset(st,j),set(st,j,0),set(st,j+1,0),f[now][st]+=v;
58             }
59         }
60         for(int j=1;j<=f[now].cnt;++j)f[now].to[j]<<=2;
61     }
62     printf("%lld\n",ans);
63 }
我依旧不会压行

 

邮递员

依旧同理。注意n或m=1。

 1 #include
 2 #define hash_mod 54321
 3 #define int_mod 100000000000000ll
 4 #define cri const register int
 5 int n,m;
 6 struct gj{
 7     long long x[5];
 8     inline void clear(){x[0]=x[1]=x[2]=x[3]=x[4]=0;}
 9     inline void print(){
10         int i;
11         for(i=4;i>=0;--i)if(x[i]){printf("%lld",x[i]);break;}
12         for(i--;i>=0;--i)printf("%014lld",x[i]);puts("");
13     }
14     inline void operator+=(const gj &b){
15         for(int i=4;i>=0;--i)x[i]+=b.x[i];
16         for(int i=0;i<=3;++i)x[i+1]+=x[i]/int_mod,x[i]%=int_mod;
17     }
18 }ans;
19 struct hash_map{
20     int fir[54321],l[54321],to[54321],cnt;gj v[54321];
21     void clear(){
22         for(int i=0;i<54321;++i)fir[i]=0,v[i].clear(),to[i]=-1;
23         cnt=0;
24     }
25     gj &operator[](const int key){
26         int pos=key%hash_mod,i;
27         for(i=fir[pos];to[i]!=key&&i;i=l[i]);
28         if(!i)to[++cnt]=key,l[cnt]=fir[pos],i=fir[pos]=cnt,v[cnt].clear();
29         return v[i];
30     }
31 }hash[2];
32 inline int find(cri st,cri pos){return st>>(pos-1<<1)&3;}
33 inline int get(cri st,cri pos){
34     int cnt=0,dirc=(find(st,pos)==1)?1:-1;
35     for(int i=pos,plug=find(st,pos);i&&i<=m+1;plug=find(st,i+=dirc)){
36         if(plug==1)cnt++;else if(plug==2)cnt--;
37         if(cnt==0)return i;
38     }
39     return -1;
40 }
41 inline void set(int &st,cri pos,cri kind){
42     st|=3<<(pos-1<<1);st^=3<<(pos-1<<1);
43     st|=kind<<(pos-1<<1);
44 }
45 void DP(cri x,cri y){
46     int now=(x-1)*m+y&1,last=now^1,tot=hash[last].cnt;
47     hash[now].clear();
48     for(int i=1;i<=tot;++i){
49         int st=hash[last].to[i],plug1=find(st,y),plug2=find(st,y+1);
50         gj v=hash[last].v[i];//printf("%d %d %d %d %d ",x,y,st,plug1,plug2);v.print();
51         if(get(st,y)==-1||get(st,y+1)==-1)continue;
52         if(!plug1&&!plug2){if(y!=m&&x!=n)set(st,y,1),set(st,y+1,2),hash[now][st]+=v;}
53         else if(plug1&&!plug2){
54             if(x!=n)hash[now][st]+=v;
55             if(y!=m)set(st,y,0),set(st,y+1,plug1),hash[now][st]+=v;
56         }
57         else if(!plug1&&plug2){
58             if(y!=m)hash[now][st]+=v;
59             if(x!=n)set(st,y,plug2),set(st,y+1,0),hash[now][st]+=v;
60         }
61         else if(plug1==1&&plug2==2){if(x==n&&y==m)ans+=v;}
62         else if(plug1==2&&plug2==1)set(st,y,0),set(st,y+1,0),hash[now][st]+=v;
63         else if(plug1==1&&plug2==1)
64             set(st,get(st,y+1),1),set(st,y,0),set(st,y+1,0),hash[now][st]+=v;
65         else if(plug1==2&&plug2==2)
66             set(st,get(st,y),2),set(st,y,0),set(st,y+1,0),hash[now][st]+=v;
67     }//printf("%d--%d--%d\n",x,y,hash[now].cnt);
68 }
69 int main(){
70     scanf("%d%d",&n,&m);
71     hash[0].clear();hash[0][0].x[0]=1;
72     if(n==1||m==1){printf("1");return 0;}
73     if(nm;
74     for(int i=1;i<=n;++i){
75         for(int j=1;j<=m;++j)DP(i,j);
76         if(i!=n){
77             int now=(i*m)&1,tot=hash[now].cnt;
78             for(int i=1;i<=tot;++i)hash[now].to[i]<<=2;
79         }
80     }
81     if(ans.x[0]==0){puts("0");return 0;}
82     ans+=ans;ans.print();
83 }
我还是不会压行

 

地板

这个插头不太一样。

对于一个L型,其实只有转弯前和转弯后两种状态。分别做1,2。

空格的话,可以把这个点作为拐角,往右下两个方向延伸出两个已经拐弯的插头。

如果只有一个1插头,那么可以选择转弯与否。

如果两个1插头碰在一起,那么它们就在这里转弯,互相抵消。

如果只有一个2插头,那么就延伸就好了。

 1 #include
 2 #include
 3 using namespace std;
 4 #define mod 20110520
 5 #define ad(a,b) add(dp[nx][set(set(st,j,a),j+1,b)],v) 
 6 void add(int &a,int b){a+=b;if(a>mod)a-=mod;}
 7 int n,m,dp[2][8048577];char s[111][111];bool a[111][111];
 8 int find(int st,int num){return st>>num*2-2&3;}
 9 int set(int st,int num,int v){return st^st&3<2-2^v<2-2;}
10 int main(){
11     scanf("%d%d",&n,&m);
12     for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
13     if(n<m){
14         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)a[j][i]=s[i][j]=='_';
15         n^=m^=n^=m;
16     }else for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)a[i][j]=s[i][j]=='_';
17     int nw=0,nx=1;dp[1][0]=1;
18     for(int i=1;i<=n;++i){
19         for(int j=1;j<=m;++j){
20             nw^=1;nx^=1;
21             for(int st=0;st<1<2+2;++st)if(dp[nw][st]){
22                 int l=find(st,j),u=find(st,j+1),v=dp[nw][st];dp[nw][st]=0;
23                 if(!a[i][j]){if(l==0&&u==0)ad(0,0);continue;}
24                 if(l==0&&u==0)ad(2,2),ad(1,0),ad(0,1);
25                 else if(l==1&&u==0)ad(0,1),ad(2,0);
26                 else if(l==0&&u==1)ad(1,0),ad(0,2);
27                 else if(l==1&&u==1)ad(0,0);
28                 else if(l==0&&u==2)ad(0,0),ad(2,0);
29                 else if(l==2&&u==0)ad(0,0),ad(0,2);
30             }
31         }
32         nw^=1;nx^=1;
33         for(int st=0;st<1<2;++st)dp[nx][st<<2]=dp[nw][st];
34         for(int st=0;st<1<2+2;++st)dp[nw][st]=0;
35     }printf("%d\n",dp[nx][0]);
36 }
然后我开始压行

 

标识设计

题意补充:笔画的宽度一定是1,棋盘不用占满,L型不能旋转。

因为不能旋转,所以只有一种插头。

但是不能占满,这个好说,只不过多了一种如果这个格子是空格的话也可以不增加新插头。

然后就退化一下上一题的代码,不过区分一下已经写了几个L就行。

 1 #include
 2 #include
 3 using namespace std;
 4 int dp[2][16666],n,m,w[111][7],stc,cts[16666],ans=-12345678,nw,nx=1;
 5 int find(int st,int num){return st>>num*2&3;}
 6 void sch(int al,int st){if(al==m+1)cts[++stc]=st;else for(int i=0;i<=2;++i)sch(al+1,st|i<2);}
 7 int set(int st,int num,int v){return st^st&3<2^v<2;}
 8 void maxi(int &a,int b){if(ab;}
 9 int rmat(int st,int p){for(int i=p+1;;++i)if(find(st,i)==2)return st-(1<2);}
10 int lmat(int st,int p){for(int i=p-1;;--i)if(find(st,i)==1)return st+(1<2);}
11 #define upd(a,b) maxi(dp[nx][set(set(st,j-1,a),j,b)],v)
12 int main(){
13     scanf("%d%d",&n,&m);memset(dp,0xa0,sizeof dp);
14     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&w[i][j]);
15     dp[nx][0]=0;sch(0,0);
16     for(int i=1;i<=n;++i){
17         for(int j=1;j<=m;++j){
18             nw^=1;nx^=1;memset(dp[nx],0xa0,sizeof dp[nx]);
19             for(int p=1,st;p<=stc;++p)if(dp[nw][st=cts[p]]>=-12345678){
20                 int p1=find(st,j-1),p2=find(st,j),v=dp[nw][st]+w[i][j];
21                 if(p1==0&&p2==0)upd(1,2),maxi(dp[nx][st],dp[nw][st]);
22                 if(p1==1&&p2==2)if(set(set(st,j-1,0),j,0)==0)maxi(ans,v);
23                 if(p1==2&&p2==1)upd(0,0);
24                 if((p1^p2)==2)upd(0,2),upd(2,0);
25                 if((p1^p2)==1)upd(0,1),upd(1,0);
26                 if(p1==1&&p2==1)maxi(dp[nx][rmat(set(set(st,j-1,0),j,0),j)],v);
27                 if(p1==2&&p2==2)maxi(dp[nx][lmat(set(set(st,j-1,0),j,0),j)],v);
28             }
29         }
30         nw^=1;nx^=1;memset(dp[nx],0xa0,sizeof dp[nx]);
31         for(int p=1,st;p<=stc;++p)if(cts[p]<1<2]=dp[nw][cts[p]];
32     }printf("%d\n",ans);
33 }
压行越发熟练

 

Park II

毒瘤界的巅峰。

理论上有3个插头,16种情况,但是情况很多重复,所以可以压成9类。

因为这一题是路径而不是回路,而且也不用占满。

为了处理路径的问题,需要引进一种新的插头,就是一端为路径的端点,另一端在轮廓线上的插头,学习LadyLex称之为独立插头3。

所有插头合并情况如下:

1,两个空格:这个格子可以不放,也可以作为拐角放一对12插头,也可以作为路径端点在右下方向之一放一个3插头。

2,两个1/2:与回路的题做法一致。

3,两个3:如果没有其它插头,那么你已经让两个自由端联通了,如果不存在其它插头,那么就可以更新答案。

4,左1上2:在回路题里这样会形成回路,但是路径题不允许回路,所以不做操作。(也就是在代码里不用写出来这种情况)

5,左2上1:与回路的题做法一致。

6,只有一个插头:另一个可以向右下方向之一转弯。

7,只有一个3插头:它可以结束了,作为路径端点,这样的话如果没有其它插头就可以更新答案了。

8,只有一个1/2插头:它可以结束了,作为路径端点,这样的话与1/2配对的2/1插头应该变成孤立的3插头。

9,有一个1/2插头,另一个是3插头:它们匹配了,这样的话与1/2配对的2/1插头应该变成孤立的3插头。
没有其它情况了。

如果宏定义+压行足够优秀,这道题不一定会比前面的题长。

 1 #include
 2 #include
 3 using namespace std;
 4 void maxi(int &a,int b){if(ab;}
 5 int set(int st,int p,int v){return st^st&3<2^v<2;}
 6 int get(int st,int p){return st>>p*2&3;}
 7 int mat(int st,int p,int v,int d,int g,int c=1){
 8     for(;;p+=d)if(get(st,p)==g+1){c--;if(!c)return st+(v-g-1<2);}
 9         else if(get(st,p)==(g^1)+1)c++;
10 }
11 int dp[2][266666],w,n,m,nx,nw=1,ans,c[4],ac[266666];
12 #define up(a,b) maxi(dp[nx][set(set(st,j-1,a),j,b)],v)
13 #define cl set(set(st,j-1,0),j,0)
14 int main(){
15     scanf("%d%d",&n,&m);
16     memset(dp,0xa0,sizeof dp);dp[nx][0]=0;
17     for(int i=1;i<=n;++i){
18         for(int j=1;j<=m;++j){
19             nw^=1;nx^=1;memset(dp[nx],0xa0,sizeof dp[nx]);scanf("%d",&w);
20             for(int st=0;st<1<2+2;++st)if(dp[nw][st]>-12345678){
21                 int p1=get(st,j-1),p2=get(st,j),v=dp[nw][st]+w;
22                 for(int q=0;q<4;++q)c[q]=0;c[p1]++;c[p2]++;
23                 if(c[0]==2)up(1,2),up(3,0),up(0,3),maxi(dp[nx][st],v-w);
24                 if(c[1]==2)maxi(dp[nx][mat(cl,j,1,1,1)],v);
25                 if(c[2]==2)maxi(dp[nx][mat(cl,j,2,-1,0)],v);
26                 if(c[3]==2)if(!cl)maxi(ans,v);
27                 if(p1==2&p2==1)up(0,0);
28                 if(c[0]==1)up(p1|p2,0),up(0,p1|p2);
29                 if(c[0]&c[3])if(!cl)maxi(ans,v);
30                 if(c[1]&(c[0]|c[3]))maxi(dp[nx][mat(cl,j,3,1,1)],v);
31                 if(c[2]&(c[0]|c[3]))maxi(dp[nx][mat(cl,j,3,-1,0)],v);
32             }
33         }
34         nx^=1;nw^=1;memset(dp[nx],0xa0,sizeof dp[nx]);
35         for(int st=0;st<1<2;++st)dp[nx][st<<2]=dp[nw][st];
36     }printf("%d\n",ans);
37 }
毒瘤巅峰的压行巅峰

 

游览计划

插头dp的话不是很好做,开5种插头记录联通块就行了。

我没有这么写,我写的是最小斯坦纳树。

好像没听过很高端?实际上就是dp。

dp[i][j][st]表式包含(i,j)这个点的一个联通块所覆盖的景点集合为st(状态压缩)。

考虑转移:

首先如果(i,j)就是景点,编号为k,那么dp[i][j][k]=0;

这个点往四周扩展:dp[x][y][st]=min(dp[i][j][st]+w[x][y])

这个点的子集合并:dp[i][j][st]=min(dp[i][j][s1]+dp[i][j][s2]-w[i][j]),其中s1与s2的并集为st。

没了。

但是第一个转移出环了。用原来B组题里的那个SPFA解决循环dp就好了。

记录转移点回溯输出方案。

 1 #include
 2 #include
 3 #include
 4 using namespace std;
 5 const int xx[]={0,0,1,-1},yy[]={1,-1,0,0};
 6 #define tx x+xx[i]
 7 #define ty y+yy[i]
 8 int w[11][11],n,m,k=1,dp[11][11][1025],pre[11][11][1025],qx[1111],qy[1111],iq[11][11],v[11][11];
 9 void SPFA(int st,int t=0){
10     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(dp[i][j][st]<999999)qx[++t]=i,qy[t]=j,iq[i][j]=1;
11     for(int h=1,x,y;x=qx[h],y=qy[h],iq[x][y]=0,h<=t;++h)for(int i=0;i<4;++i)
12         if(tx&&ty&&tx<=n&&ty<=m&&dp[tx][ty][st]>dp[x][y][st]+w[tx][ty]){
13             dp[tx][ty][st]=dp[x][y][st]+w[tx][ty];pre[tx][ty][st]=x<<20|y<<10|st;
14             if(!iq[tx][ty])iq[qx[++t]=tx][qy[t]=ty]=1;
15         }
16 }
17 void dfs(int x,int y,int st){
18     v[x][y]=1;int px=pre[x][y][st]>>20,py=pre[x][y][st]>>10&1023,pst=pre[x][y][st]&1023;
19     if(!pre[x][y][st])return;
20     dfs(px,py,pst);if(x==px&&y==py)dfs(px,py,st^pst);
21 }
22 int main(){
23     scanf("%d%d",&n,&m);memset(dp,0x3f,sizeof dp);
24     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&w[i][j]);
25     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(!w[i][j])dp[i][j][k]=0,k<<=1;
26     for(int st=1;stst){
27         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int s=st&st-1;s;s=s-1&st)
28             if(dp[i][j][st]>dp[i][j][s]+dp[i][j][st^s]-w[i][j])
29                 dp[i][j][st]=dp[i][j][s]+dp[i][j][st^s]-w[i][j],pre[i][j][st]=i<<20|j<<10|s;
30         SPFA(st);
31     }
32     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(!w[i][j]){
33         dfs(i,j,k-1);printf("%d\n",dp[i][j][k-1]);
34         for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=m;++j)putchar(w[i][j]?(v[i][j]?'o':'_'):'x');
35         return 0;
36     }
37 }
难写所以没压行

你可能感兴趣的:([专题总结]初探插头dp)