21.6.23 t2

tag:轮廓线dp


手玩一下会发现,最少需要4个镜子才能减少答案,多玩一下就能发现,减少的答案就等于镜子形成的回路长度。

\(ans=4nm-2len\)

为了计算这个东西,可以理解为,从镜子射出光线,然后贡献就是那些没有到达边界的光线的总长度。

然后问题就变成了,放 \(k\) 个镜子,形成的封闭光路最多有多长(可以不连通)

直接轮廓线 \(dp\) 就行,分讨见代码。

\(O(nm2^{\min(n,m)})\)


最后记得求前缀 \(\min\)

#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
    char ch; bool flag=false;
    while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
    for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
    if(flag)n=-n;
}

int n, m;
int f[2][36][1<<6];

char a[10][10], tmp[10][10];

inline void upd(int &a, int b){if(b>a) a = b;}

int main(){
    // freopen("2.in","r",stdin);
    // freopen("2.out","w",stdout);
    int T; Read(T);
    while(T--){
        Read(n); Read(m);
        for(int i=0; i<n; i++) scanf("%s",a[i]);
        if(n<m){
            for(int i=0; i<n; i++) for(int j=0; j<m; j++) tmp[j][n-i-1] = a[i][j], a[i][j] = 0;
            swap(n,m);
            for(int i=0; i<n; i++) for(int j=0; j<m; j++) a[i][j] = tmp[i][j], tmp[i][j] = 0;
        }
        // for(int i=0; i<n; i++) printf("%s\n",a[i]);puts("");
        char opt = 0; memset(f[opt],-1,sizeof f[opt]);
        f[opt][0][0] = 0; int tp = 1<<(m+1);
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                char nxt = opt^1;
                memset(f[nxt],-1,sizeof f[nxt]);
                for(int k=0; k<=i*m+j; k++) for(int S=0; S<tp; S++) if(f[opt][k][S]>=0){
                    char v1 = S>>j&1, v2 = S>>(j+1)&1, nxtS;
                    int val = f[opt][k][S];
                    
                    if(v1==0 and v2==0){
                        // not place
                        upd(f[nxt][k][S],val);
                        // place "/"
                        if(a[i][j]=='1' and i!=n-1 and j!=m-1)
                            nxtS = S,
                            nxtS |= 1<<j,
                            nxtS |= 1<<(j+1),
                            upd(f[nxt][k+1][nxtS],val+1);
                    }
                    
                    if(v1==0 and v2==1){
                        // not place
                        if(i!=n-1)
                            nxtS = S,
                            nxtS ^= 1<<(j+1),
                            nxtS |= 1<<j,
                            upd(f[nxt][k][nxtS],val+1);
                        // place "\"
                        if(a[i][j]=='1' and j!=m-1)
                            upd(f[nxt][k+1][S],val+1);
                    }

                    if(v1==1 and v2==0){
                        // not place
                        if(j!=m-1)
                            nxtS = S,
                            nxtS ^= 1<<j,
                            nxtS |= 1<<(j+1),
                            upd(f[nxt][k][nxtS],val+1);
                        // place "\"
                        if(a[i][j]=='1' and i!=n-1)
                            upd(f[nxt][k+1][S],val+1);
                    }

                    if(v1==1 and v2==1){
                        // not place (or place "\")
                        if(i!=n-1 and j!=m-1)
                            upd(f[nxt][k][S],val+2);
                        // place "/"
                        if(a[i][j]=='1')
                            nxtS = S,
                            nxtS ^= 1<<j,
                            nxtS ^= 1<<(j+1),
                            upd(f[nxt][k+1][nxtS],val+1);
                    }
                }
                opt ^= 1;
            }
            if(i!=n-1){
                char nxt=opt^1;
                memset(f[nxt],-1,sizeof f[nxt]);
                for(int k=0; k<=(i+1)*m; k++) for(int S=0, tpS=(1<<m); S<tpS; S++) if(f[opt][k][S]>=0) f[nxt][k][S<<1] = f[opt][k][S];
                opt ^= 1;
            }
        }
        f[opt][0][0] = max(f[opt][0][0],0);
        for(int k=1; k<=n*m; k++) upd(f[opt][k][0],f[opt][k-1][0]);
        for(int k=0; k<=n*m; k++) printf("%d ",n*m*4-max(f[opt][k][0],0)*2);puts(""); 
    }
    return 0;
}
posted @ 2021-06-24 16:34  oisdoaiu  阅读(38)  评论(0编辑  收藏  举报