简单DP就是图论 !

P1541 [NOIP2010 提高组] 乌龟棋

方程想脑抽了,硬是觉得四维循环的DP顺序不对

于是打算用位置递增的顺序正推枚举状态

每个状态用一个五元组 (s1,s2,s3,s4,f) 表示,前四个表示到目前各种牌用了几张,f 表示最大价值

因为 由 s 1~4 可以确定当前的位置,且前面的出牌顺序不会影响后面的决策,

所以该表示法能够完整表示一个状态,并进行正推

可以在每个位置建一个vector (本处使用邻接表)记录该位置的所有状态

顺序枚举每一个位置,枚举到达该位置的所有状态,并利用类似 bfs 的马蜂更新可到达的状态

因为每个状态只会枚举到一次,所以总复杂度仅与状态总数有关 ,上限为 O( 40 4),可以通过

为了防止状态冗余(多次枚举),将f 改写成记忆化数组

(手多还写了个邻接表空间回收)、

code:

//P1613
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define uLL unsigned long long
#define rint register int
#define itn int
#define mem(x) memset(x,0,sizeof(x))
inline LL read(){
   LL x=0,ff=1;char ss=getchar();
   while(ss<'0'||ss>'9'){if(ss=='-')ff=-1;ss=getchar();}
   while(ss<='9'&&ss>='0'){x=x*10+ss-'0';ss=getchar();}
   return ff*x;
}
inline void writee(LL x){
   if(x<0){putchar('-');x=-x;}
   if(x>9)writee(x/10);
   putchar(x%10+'0');
}
inline void write(LL x){writee(x);putchar('\n');}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void ckmax(int &x,int y){if(y>x)x=y;}


const int N=361,M=1e6+10;
const LL inf=1e14,mod=998244353;
int n,m,q;
int a[N],b[5];
int Head[N],Next[M<<1],ver[M<<1][5],f[42][42][42][42],tot=1;
int dtop,dsta[M];
int ans=0;
void del(int e){
    Next[e]=0;for(rint i=1;i<=4;i++)ver[e][i]=0;
    dsta[++dtop]=e;
}
int dis(int* s){
    int dd=1;
    for(rint i=1;i<=4;i++)dd+=s[i]*i;
    return dd;
}
void Add(int x,int* s,int d){
    if(f[s[1]][s[2]][s[3]][s[4]]){ckmax(f[s[1]][s[2]][s[3]][s[4]],d);return;}
    int p=dtop?dsta[dtop--]:++tot;
    Next[p]=Head[x];Head[x]=p;f[s[1]][s[2]][s[3]][s[4]]=d;
    for(rint i=1;i<=4;i++)ver[p][i]=s[i];
}


int main(){
    //freopen("P1541_2.in","r",stdin);
    n=read();m=read();
    for(rint i=1;i<=n;i++)a[i]=read();
    for(rint i=1;i<=m;i++)b[read()]++;
    int s[5],nxt=0;
    s[1]=s[2]=s[3]=s[4]=0;
    Add(1,s,a[1]);
    for(rint i=1;i<=n;i++){
        for(int e=Head[i];e;e=nxt){
            for(rint j=1;j<=4;j++)s[j]=ver[e][j];
            int k=f[s[1]][s[2]][s[3]][s[4]];
            for(rint j=1;j<=4;j++)
                if(s[j]+1<=b[j]){
                    s[j]++;Add(dis(s),s,k+a[dis(s)]);
                    s[j]--;
                }
            nxt=Next[e];del(e);
        }
    }write(f[b[1]][b[2]][b[3]][b[4]]);
}

 

 

posted @ 2021-05-31 20:56  Sherlockk  阅读(94)  评论(0)    收藏  举报