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

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

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

50pts

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

100pts

状压DP

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

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

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

对于第二种转移:

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

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

代码

#include<stdio.h>
#include<cstring>
#include<iostream>
#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<<u-v)));
    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;
}

猜你喜欢

转载自blog.csdn.net/ArliaStark/article/details/81903911