洛谷题目传送门c++
顺便提一下题意有一个地方不太清楚,就是若是输出No
还要输出最少须要添加多少张牌才能知足要求。蒟蒻考完之后发现四个点Too short on line 2
。。。spa
比较须要技巧的搜索code
既然是同一个花色要连续,那就枚举每个花色在哪一段区间连续并选中四个区间,累计每一个点数的选中次数。get
最后来一个\(O(13)\)的\(\text{check}\),首先每一个点数选中次数要很多于已有的个数。接着,只有全部点数的选中次数和已有点数相等时,才能判为'Yes',而后统计某张牌的花色的区间未包含这张牌的总数更新答案。不然判为'No',而后统计每一个点数选多了的总数更新答案。it
总复杂度\(\binom{13}{2}^4* 13=481195728\),超过了\(10^8\),确定须要剪枝。class
可行性剪枝:每一个点数选中次数之和很多于原有牌的总数。搜索
最优性剪枝:实时统计选多了的总数,在No
的状态下,若是超过答案则剪掉;在Yes
的状态下,只要不为\(0\)就剪掉。技巧
700+ms比标程快多了。可能标程比较良心没加什么剪枝。统计
#include<bits/stdc++.h> #define R register int using namespace std; const int N=99; int a[N],b[N],l[N],r[N],cnt[N],n,now1,ans=N,ans1=N; char s[N]; void dfs(R h,R lef){//lef为n-当前已选中总次数 if(h==5){ R now=0; for(R i=1;i<=13;++i){ if(cnt[i]>0)return;//不合法 now|=cnt[i]<0; } if(now){ans1=now1;return;}//No状态 for(R i=1;i<=n;++i)//Yes状态,统计答案 if((l[a[i]]>b[i]||r[a[i]]<b[i])&&++now==ans)return; ans=now;ans1=1;return;//注意ans1=1的剪枝做用 } for(R i=max(lef-(4-h)*13,0),j,rr;i<=13;++i){//枚举区间长度,可行性剪枝 if(i==0){l[h]=r[h]=0;dfs(h+1,lef);continue;} for(rr=i;rr<=13;++rr){//枚举右端点 for(j=rr-i+1;j<=rr;++j)now1+=--cnt[j]<0;//动态维护当前选多了的总数 if(now1<ans1)l[h]=(r[h]=rr)-i+1,dfs(h+1,lef-i);//最优性剪枝 for(j=rr-i+1;j<=rr;++j)now1-=++cnt[j]<=0; } } } int main(){ scanf("%d",&n); for(R i=1;i<=n;++i){ scanf("%d%s",&a[i],s); if(s[0]=='A')b[i]=1;//把点数处理一下 else if(s[0]=='1')b[i]=10; else if(s[0]=='J')b[i]=11; else if(s[0]=='Q')b[i]=12; else if(s[0]=='K')b[i]=13; else b[i]=s[0]-'0'; ++cnt[b[i]]; } dfs(1,n); if(ans!=N)printf("Yes\n%d\n",ans); else printf("No\n%d\n",ans1); return 0; }