【学习笔记】NOI 模拟赛 t3 point

点这里看题目

考场上也没想到这道 t 3 t3 t3竟是整场比赛最可做的一道题。但是拿了全场最高的部分分,这是好的。

唉,学了插头 d p dp dp但是却无法将其运用,我真傻,真的。

部分分已经很好的提示了这道题的正解是状压。先考虑 d > 8 d>8 d>8,也就是我考场上没想出来的部分。

不妨将原问题做细微的变动,令 d ← 2 d d\gets 2d d2d,那么 x x x可以移动到 x x x x + 2 d x+2d x+2d两个位置,这样做的好处在于所有点都是朝一个方向运动。

以往我们对坐标的认识都是很大而不好处理的,但是这道题非常特殊,注意到 x i ≤ 150 x_i\le 150 xi150,因此可以直接看成是大小 ≤ 150 \le 150 150的棋盘,特别的,我们将 x , x + d , x + 2 d , . . . x,x+d,x+2d,... x,x+d,x+2d,...排成一个横排,这样得到了一个宽 ≤ ⌊ x i d ⌋ \le \lfloor\frac{x_i}{d}\rfloor dxi的矩阵。

注意到 ⌊ x i d ⌋ ≤ 8 \lfloor\frac{x_i}{d}\rfloor\le 8 dxi8,这提示我们按行进行状压。但是同一行的坐标在数轴上不是连续的,注意到我们在插头 d p dp dp中处理的路径也不是连续的,但是我们可以将它拼接起来,那么类似的,我们也可以看成是数轴上有若干个端点朝左右方向扩展,新建一个端点的代价是 a a a,扩展一个单位的代价是 b b b。甚至这道题目的情况也远没有插头 D P DP DP复杂,只是这个想法比较隐晦而已。

观察一下这个表格,同一列在坐标轴上的位置是固定的,因此可以只用记录两个端点是否被线段覆盖。一个棋子也只能移动到同一行相邻的位置,因此情况并不复杂。那么这部分就做完了,复杂度 O ( max ⁡ ( x i ) 2 16 ) O(\max(x_i)2^{16}) O(max(xi)216)

最后再来补充一下 d ≤ 8 d\le 8 d8的部分。不得不说这个部分分设置得非常好。考场上想了一个巨麻烦的方法,但是其实没有必要,就直接记录后 d d d个位置那些点有棋子,以及前一个位置有没有被覆盖即可,这其实就是我考场的做法,但是考场上好像写复杂了。复杂度同样是 O ( max ⁡ ( x i ) 2 16 ) O(\max(x_i)2^{16}) O(max(xi)216)

代码比想象中的要长一点。注意数组不要开小了。

#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
int n,d,a,b,X[155],now[2][1<<18],nxt[2][1<<18],vispoints[305];
int h,w,now2[1<<9],nxt2[1<<9],points[305][20],res=0x3f3f3f3f;
void chmin(int &x,int y){x=min(x,y);}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>d>>a>>b,d<<=1;
    for(int i=0;i<n;i++)cin>>X[i];
    sort(X,X+n);
    n=unique(X,X+n)-X;for(int i=n-1;i>=0;i--)X[i]-=X[0];
    if(d<=18){
        memset(now,0x3f,sizeof now);
        now[0][0]=0;
        for(int i=0;i<n;i++)vispoints[X[i]]=1;
        for(int i=0;i<=X[n-1]+d;i++){
            memset(nxt,0x3f,sizeof nxt);
            if(!vispoints[i]){
                for(int j=0;j<1<<d;j++){
                    if(j&1){
                        chmin(nxt[1][j>>1],now[0][j]+a);
                        chmin(nxt[1][j>>1],now[1][j]+min(a,b));
                    }
                    else{
                        chmin(nxt[1][j>>1],now[0][j]+a);
                        chmin(nxt[1][j>>1],now[1][j]+min(a,b));
                        chmin(nxt[0][j>>1],now[0][j]);
                        chmin(nxt[0][j>>1],now[1][j]);
                    }
                }
            }
            else{
                for(int j=0;j<1<<d;j++){
                    if(j&1){
                        chmin(nxt[1][j>>1],now[0][j]+a);
                        chmin(nxt[1][j>>1],now[1][j]+min(a,b));
                    }
                    else{
                        chmin(nxt[1][j>>1],now[0][j]+a);
                        chmin(nxt[1][j>>1],now[1][j]+min(a,b));
                        chmin(nxt[0][(j>>1)^(1<<d-1)],now[0][j]);
                        chmin(nxt[0][(j>>1)^(1<<d-1)],now[1][j]);
                    }
                }
            }
            memcpy(now,nxt,sizeof nxt);
        }
        cout<<now[0][0];
    }
    else{
        h=d,w=X[n-1]/d+2;
        assert(2*w<=18);
        for(int i=0;i<n;i++){
            points[X[i]%d][X[i]/d]=1;
            assert(X[i]/d<w-1);
            assert(X[i]%d<h);
        }
        for(int s=0;s<1<<w;s++){
            int ok=1;
            for(int i=0;i<w-1;i++){
                if(points[0][i]&&!((s>>i&1)|(s>>i+1&1))){
                    ok=0;
                    break;
                }
            }
            if(!ok)continue;
            int tmp=__builtin_popcount(s);
            memset(now2,0x3f,sizeof now2),now2[s]=tmp*a;
            for(int i=1;i<h;i++){
                for(int j=0;j<w;j++){
                    memset(nxt2,0x3f,sizeof nxt2);
                    for(int k=0;k<1<<w;k++){
                        if(j&&points[i][j-1]&&!(k>>j-1&1)){
                            if(k>>j&1){
                                chmin(nxt2[k],now2[k]+min(a,b));
                            }
                            else{
                                chmin(nxt2[k^(1<<j)],now2[k]+a);
                            }
                        }
                        else{
                            if(k>>j&1){
                                chmin(nxt2[k],now2[k]+min(a,b));
                                chmin(nxt2[k^(1<<j)],now2[k]);
                            }
                            else{
                                chmin(nxt2[k^(1<<j)],now2[k]+a);
                                chmin(nxt2[k],now2[k]);
                            }
                        }
                    }
                    memcpy(now2,nxt2,sizeof nxt2);
                }
            }
            for(int i=0;i<1<<w;i++){
                for(int j=0;j<w-1;j++){
                    if((i>>j&1)&&(s>>j+1&1)&&a>=b){
                        now2[i]-=a,now2[i]+=b;
                    }
                }
                chmin(res,now2[i]);
            }
        }
        cout<<res;
    }
}

你可能感兴趣的:(学习,笔记,算法)