1. 程式人生 > 其它 >21.6.23 t2

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;
}