1. 程式人生 > >2018.11.01 bzoj4325: NOIP2015 鬥地主(貪心+搜尋)

2018.11.01 bzoj4325: NOIP2015 鬥地主(貪心+搜尋)

傳送門
原來一直以為是一道大模擬。
沒想到是一道搜尋+最優性剪枝
如何搜最優呢?
我們考慮怎麼最快出完。
大概是應該儘量出當前能出出去最多的吧。
於是我們選擇優先出順子。
這樣做有什麼好處呢?
我們會發現除了順子以外的牌都能夠直接算最少需要出幾輪。
因此把順子出完之後更新答案就行了。
於是出牌優先順序:順子>四帶二>四帶一>三帶二>三帶一>對子>單牌
程式碼:

#include<bits/stdc++.h>
using namespace std;
int card[4]={0,5,3,2},r,n,a[15],ans,tot[5];
inline int calc(){ int ret=0; memset(tot,0,sizeof(tot)); for(int i=0;i<=14;++i)if(i^1)++tot[a[i]]; while(tot[4]&&tot[2]>=2)++ret,--tot[4],tot[2]-=2; while(tot[4]&&tot[1]>=2)++ret,--tot[4],tot[1]-=2; while(tot[3]&&tot[2])++ret,--tot[3],--tot[2]; while(tot[3]&&
tot[1])++ret,--tot[3],--tot[1]; return ret+tot[1]+tot[2]+tot[3]+tot[4]; } inline void dfs(int dep){ if(dep>=ans)return; for(int same=3,j,len;same;--same){ for(int i=3;i<=13;++i){ j=i; while(a[j]>=same&&j<=14)++j; --j; if((len=j-i+1)<card[same])continue; for
(int k=i;k<=i+card[same]-2;++k)a[k]-=same; for(int k=i+card[same]-1;k<=j;++k)a[k]-=same,dfs(dep+1); for(int k=i;k<=j;++k)a[k]+=same; } } ans=min(ans,dep+calc()); } int main(){ scanf("%d%d",&r,&n),ans=n; for(int i=1,x,y;i<=r;++i,memset(a,0,sizeof(a)),ans=n){ for(int j=1;j<=n;++j)scanf("%d%d",&x,&y),x=x==1?14:x,++a[x]; dfs(0),printf("%d\n",ans); } return 0; }