contest 0820 calc [DP][记忆化搜索]

contest 0820 calc [DP][记忆化搜索]

当时竟然不知道暴力怎么打,于是就只骗了5分 Q^Q

50pts

先把每种合法的边(端点编号差<=lim的边)放到一个数组里面,然后暴搜,最后判断选出来的边构成的图是否合法(每个点的度数都是偶数)
期望得分:40~50pts

100pts

状压DP

我们强制规定边都有方向(从编号大的指向编号小的;也可以反着来)。用 f[u][v][k][s] f [ u ] [ v ] [ k ] [ s ] 表示从 u u 指向 v v ,剩余 k k 条边可用,且编号为 [ulim+1,u] [ u − l i m + 1 , u ] 的点的度数奇偶状态为s(设1为奇,0为偶)。

每次有两种转移: u u v v 之间再连一条边; u u v v 之间不连边,向 v1 v − 1 连一条边。

对于第一种转移,只要还有边可以选就可行。

对于第二种转移:

  • 如果有 v1 v − 1 这个点且 v1 v − 1 u u 的差 <=lim <= l i m ,那么就可以向 f[u][v1][k][s] f [ u ] [ v − 1 ] [ k ] [ s ′ ] 转移

  • 如果上述转移不可行,那么说明能够与 u u 相连的点已经讨论完了,要开始讨论 u1 u − 1 号点了;如果 u u 号点的度数为偶数,就可以向 u1 u − 1 号点转移,即 f[u1][u2][k][s] f [ u − 1 ] [ u − 2 ] [ k ] [ s ′ ]

代码

#include
#include
#include
#define N 35
#define Mod 998244353
using namespace std;
int n,m,lim,f[N][N][N][513];
int Add(int x,int y){return (x+y>=Mod)?(x+y-Mod):(x+y);}
int DFS(int u,int v,int Rest,int s){
    if(!Rest)return s==0;if(u==1)return 0;
    if(f[u][v][Rest][s]!=-1)return f[u][v][Rest][s];
    int t=0;t=Add(t,DFS(u,v,Rest-1,s^1^(1<if(v>1 && u-v+1<=lim)t=Add(t,DFS(u,v-1,Rest,s));
    else if(~s&1)t=Add(t,DFS(u-1,u-2,Rest,s>>1));
    return f[u][v][Rest][s]=t;
}
int main(){
    memset(f,-1,sizeof(f));
    scanf("%d%d%d",&n,&m,&lim);
    printf("%d",DFS(n,n-1,m,0));
    return 0;
}

你可能感兴趣的:(动态规划与递推,状态压缩)