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 指向,剩余 k k 条边可用,且编号为的点的度数奇偶状态为s(设1为奇,0为偶)。
每次有两种转移: u u 和之间再连一条边; u u 和之间不连边,向 v−1 v − 1 连一条边。
对于第一种转移,只要还有边可以选就可行。
对于第二种转移:
如果有 v−1 v − 1 这个点且 v−1 v − 1 与 u u 的差,那么就可以向 f[u][v−1][k][s′] f [ u ] [ v − 1 ] [ k ] [ s ′ ] 转移
如果上述转移不可行,那么说明能够与 u u 相连的点已经讨论完了,要开始讨论号点了;如果 u u 号点的度数为偶数,就可以向号点转移,即 f[u−1][u−2][k][s′] 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;
}