【无标题】2024.3.14

2024.3.14 【深处暖春 ,才会怕冷。】

Thursday 二月初五


<BGM = “Night Crusing x Time to Pretend–ZsFlovexl”>

状压DP

oiwiki中的解释

T1 P2566 SCOI2009 围豆豆

先贴代码:

//2024.3.14
//by white_ice
#include<bits/stdc++.h>
using namespace std;
#define itn int
const int  oo = 12;

int jntm(itn a,int b){return a>b?a:b;}

int n,m,d;
int out,f[oo][oo][1<<oo],da[oo],val[1<<oo];

struct nod{
    int x,y,s;
    nod(int a=0,int b=0,int c=0){
        x=a,y=b,s=c;
    }
}b[oo*oo];
char c[oo][oo];
int xx[4]={0,-1,0,1},yy[4]={-1,0,1,0},ax[oo],ay[oo];
int ans=-0x3f3f3f3f;
int vis[oo][oo][1<<oo];
int solve(int mx,int my,int nx,int ny,int ms){
    int ns=ms;
    for(int i=1;i<=d;i++){
        if(((mx==ax[i] && nx<ax[i]) || (mx<ax[i] && nx==ax[i])) && ny>ay[i]){
            ns^=(1<<(i-1));
        }
    }
    return ns;
}
void spfa(int x,int y){
    queue<nod> q;
    q.push(nod(x,y,0));
    memset(f,0x3f,sizeof(f));
    f[x][y][0]=0;

    while(!q.empty()){
        nod p=q.front();
        q.pop();
        int mx=p.x,my=p.y,ms=p.s;
        vis[mx][my][ms]=0;
        for(int i=0;i<4;i++){
            int nx=mx+xx[i],ny=my+yy[i];
            if(nx<1||ny<1||nx>n||ny>m||(c[nx][ny]>='1'&&c[nx][ny]<='9')||c[nx][ny]=='#')
				continue;
            int ns=ms;
            if(i&1) ns=solve(mx,my,nx,ny,ms);
            if(f[mx][my][ms]<f[nx][ny][ns]){
                f[nx][ny][ns]=f[mx][my][ms]+1;
                
                if(vis[nx][ny][ns]==0){
                    vis[nx][ny][ns]=1;
                    q.push(nod(nx,ny,ns));
                }
            }
        }
    }
    
    for(int i=0;i<out;i++)
        ans=jntm(ans,val[i]-f[x][y][i]);
    
}
int main(){
    scanf("%d%d%d",&n,&m,&d);
    
    for(int i=1;i<=d;i++)
        scanf("%d",&da[i]);
    
    out=1<<d;
    
    for(int i=0;i<out;i++)
        for(int j=1;j<=d;j++)
            if(i&(1<<(j-1))) 
				val[i]+=da[j];
        
    for(int i=1;i<=n;i++)
        scanf("%s",c[i]+1);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]>'0' && c[i][j]<='9'){
                int now=c[i][j]-'0';
                ax[now]=i;
				ay[now]=j;
            }
        }
    }
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='0'){
                spfa(i,j);
            }
        }
    }
    
    printf("%d\n",ans);
    
    return 0;
}

这个题其实很重要的一点就是射线定理

从一个点出发,向右发射一条射线,通过射线与路径轮廓线的交点个数(竖直方向移动的交点)可以判断是否被围在封闭图形中,射线定理,即交点数为奇数则在该封闭图形中。

直接遍历所有豆豆判断相交就可以

最终的状态转移方程为:ans=max(ans,val[i]-f[ii][jj][i]) val[i]是我们预处理出来的状态为i时豆子的总价值

T2 P2150 NOI2015 寿司晚宴
//2024.3.14
//by white_ice
#include<bits/stdc++.h>
using namespace std;
#define itn int 
const int oo = 1e9+9;
const int op = 302;

int p[10] = {0,2,3,5,7,11,13,17,19,0};

int n,mod;

struct nod {
    itn val,big,s;
    void init(){
        itn tmp = val;big = -1;
        for (itn i=1;i<=8;i++){
            if (tmp%p[i])
                continue;
            s|=(1<<i-1);
            while(tmp%p[i]==0)
                tmp/=p[i];
        }
        if (tmp!=1)
            big = tmp;
    }
}st[502];

bool cmp(nod a,nod b){return a.big<b.big;}
itn cou(itn l,int r){l+=r;return l>=mod?l-mod:l;}

itn f[op][op],fp[op][op],fq[op][op];

itn main(){
	cin >> n >> mod;
    for (int i=2;i<=n;i++)
        st[i-1].val = i,st[i-1].init();
    sort(st+1,st+n,cmp);
    f[0][0] = 1;
    for (itn i=1;i<n;i++){
        if(i==1||st[i].big!=st[i-1].big||st[i].big==-1){
            memcpy(fp,f,sizeof(fp));
            memcpy(fq,f,sizeof(fq));
        }
        for(itn j=255;j>=0;j--){
            for(itn k=255;k>=0;k--){
                if(j&k)
                    continue;
                if((st[i].s&j)==0)
                    fq[j][k|st[i].s]=cou(fq[j][k|st[i].s],fq[j][k]);
                if((st[i].s&k)==0)
                    fp[j|st[i].s][k]=cou(fp[j|st[i].s][k],fp[j][k]);
            }
        }
        if(i==n-1||st[i].big!=st[i+1].big||st[i].big==-1){
            for(itn j=0;j<=255;j++){
                for(itn k=0;k<=255;k++){
                    if(j&k)
                        continue;
                    f[j][k]=cou(fp[j][k],cou(fq[j][k],mod-f[j][k]));
                }
            }
        }    
    }

    long long out = 0;
    for (itn j=0;j<=255;j++)
        for (int k=0;k<=255;k++)
            if ((j&k)==0&&f[j][k])
                out = cou(out,f[j][k]);

    cout << out;
    return 0;
}

对于这个题,不难想到用所有素数将500以内的数直接解开,因为500并不大。

用因子的存在表示数的状态,再轮流模拟两个人取数就可以了。

T4 P3604 美好的每一天
//2024.3.14
//by white_ice
#include<bits/stdc++.h>
using namespace std;
#define itn int
const int oo = 60004;

int n,m;
int base;
long long st[oo];

struct nod{
    itn l,r,id;
    bool operator <(nod i)const{
        return l/base==i.l/base?r<i.r:l<i.l;
    }
}sp[oo];

itn out[oo];

itn cnt[(1<<26)],ned;

void ins(itn x){
    ned += cnt[st[x]];
    cnt[st[x]]++;
    for (int i=0;i<26;i++)
        ned+=cnt[st[x]^(1<<i)];
    return ;
}

void del(itn x){
    cnt[st[x]]--;
    ned -= cnt[st[x]];
    for (itn i=0;i<26;i++)
        ned-=cnt[st[x]^(1<<i)];
    return;
}

int main(){
    cin >> n >> m;

    base = 3*sqrt(n);

    for (itn i=1;i<=n;i++){
        char c;
        cin >> c;
        st[i] = 1<<(c-'a');
        st[i]^=st[i-1];
    }


    for (int i=1;i<=m;i++){
        cin >> sp[i].l >> sp[i].r;
        sp[i].id = i;
    }

    sort(sp+1,sp+m+1);
    
    itn l = 1,r = 0;
    for (itn i=1;i<=m;i++){
        itn x = sp[i].l-1;
        itn y = sp[i].r;
        itn id = sp[i].id;
        while (l<x)del(l++);
        while (l>x)ins(--l);
        while (r>y)del(r--);
        while (r<y)ins(++r);
        out[id] = ned;
    }
    for (itn i=1;i<=m;i++)
        cout << out[i] << endl;

    return 0;
}

对于一个回文串,其只可能是所有字母都是偶数个或者仅有一个字母是奇数个,所以异或这种操作就显得非常奇技淫巧好用。

将26个英文字母设为对应的唯一2的整数次方数,于是就实现了状态压缩。

再运用前缀(异或)和这种方式进行维护就可以快速求解了。

求解部分是比较标准的莫队算法。

(其实本题不是DP,但是我真的很喜欢状态压缩和暴力数据结构。。。)

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值