怎么好像没什么人写 $dp$ ...?
设 $f[i][j][k][0/1]$ 表示当前处理完前 $1$ 到 $i$ 的数,上一位开始的顺子有 $j$ 个,当前位开始的顺子有 $k$ 个,是否已经有雀头,的情况下能不能胡
因为连续三个顺子其实等价于三个刻字,所以我们只要考虑顺子小于 $3$ 的情况,即 $j,k$ 都小于 $3$
然后枚举下一位的顺子数量并把当前位的顺子补好,剩下的全做刻字,顺便处理一下雀头就行
然后枚举每种可能的听牌,跑一遍 $dp$ 看看有没有解
具体怎么 $dp$ 也挺好想的,看代码注释吧...
复杂度 $O(不会算)$,反正跑得飞快
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
const int N=507;
int n,m,a[N];
int f[N][3][3][2];
bool work()
{
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(int i=0;i<n;i++)//当前处理完第i位,考虑下一位
for(int j=0;j<3;j++)//以上一位开头有j个顺子
for(int k=0;k<3;k++)//当前为开头有k个顺子
for(int p=0;p<2;p++)//当前是否有雀头
{
if(!f[i][j][k][p]) continue;//剪枝
for(int l=0;l<=min(a[i+1]-j-k,2);l++)//枚举下一位顺子数量
if((a[i+1]-j-k-l)%3==0)//剩下的全做刻字
f[i+1][k][l][p]=1;
if(!p)//考虑留两个做雀头
for(int l=0;l<=min(a[i+1]-j-k-2,2);l++)//同上
if((a[i+1]-j-k-l-2)%3==0)
f[i+1][k][l][1]=1;
}
return f[n][0][0][1];//看看最后有没有解
}
vector <int> ans;
int main()
{
n=read(),m=read();
for(int i=1;i<=m*3+1;i++) a[read()]++;
for(int i=1;i<=n;i++)//枚举听牌
{
a[i]++;
if(work()) ans.push_back(i);
a[i]--;
}
int len=ans.size();
if(!len) { printf("NO\n"); return 0; }
for(int i=0;i<len;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}