题解 P5301 【[GXOI/GZOI2019]宝牌一大堆】
生活随笔
收集整理的這篇文章主要介紹了
题解 P5301 【[GXOI/GZOI2019]宝牌一大堆】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這道題除了非常惡心以外也沒有什么非常讓人惡心的地方
當然一定要說有的話還是有的,就是這題和咱 ZJOI 的 mahjong 真的是好像的說~
于是就想說這道題出題人應該被 錒 掉
noteskey
整體的思路就是特判國士無雙和七對子,然后 dp 搞普通的胡牌
dp 狀態設計和樓上大佬說的一樣,就是用一個五維的 \(f[i][j][k][l][p]\) 表示當前處理了前 i 種類型的牌,存在 j 個 面子/杠子 ,以 i-1 開頭的順子要選 k 個,以 i 開頭的面子要選 l 個,以及當前是否有 雀頭 (用 p 表示)
然后轉移就非常的暴力了,反正這里的數據范圍也比較小,枚舉下狀態轉移就好了
總的來說就是道 語文 + 碼農 + dp 題,雖說沒什么思維難度但我不見得能想出來
watch out
這題的字符串讀入還是比較毒瘤的...要稍微注意一下不然可能會出事
code
這壓行是同樣的味道呢~
//by Judge #include<bits/stdc++.h> #define Rg register #define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i) #define ll long long using namespace std; #ifndef Judge #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) #endif char buf[1<<21],*p1=buf,*p2=buf; inline bool cmax(ll& a,ll b){return a<b?a=b,1:0;} inline int read(){ int x=0,f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void reads(string& s){ char c=getchar();for(;!isalpha(c)&&!isdigit(c);c=getchar()); s="";for(;isalpha(c)||isdigit(c);c=getchar()) s+=c; } char sr[1<<21],z[20];int CCF=-1,Z; inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;} inline void print(ll x,char chr='\n'){if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;while(z[++Z]=x%10+48,x/=10);while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr; } int t,cnt,a[41],b[41],C[5][5]; ll f[41][5][3][3][2],tp[41]; int gs[14]={0,1,9,10,18,19,27,28,29,30,31,32,33,34}; string c; inline int id(){ if(c[0]=='B') return 34;if(c[0]=='E') return 28; if(c[0]=='S') return 29; if(c[0]=='W') return 30;if(c[0]=='N') return 31; if(c[0]=='Z') return 32; if(c[0]=='F') return 33;if(c[1]=='m') return c[0]-48; if(c[1]=='p') return c[0]-39; return c[0]-30; } int main(){ C[0][0]=1;fp(i,1,4){ C[i][0]=1;fp(j,1,i) C[i][j]=C[i-1][j-1]+C[i-1][j];}fp(T,1,read()){memset(a,0,sizeof a);memset(b,0,sizeof b);memset(f,0,sizeof f);while(1){ reads(c);if(c[0]=='0') break;else ++a[id()];}while(1){ reads(c);if(c[0]=='0') break;else b[id()]=1;}fp(i,1,34) a[i]=4-a[i];ll ans=0;fp(i,1,13){ ll tmp=1; //枚舉出現兩次的牌 fp(j,1,13) //枚舉 13 種牌 if(i==j)if(a[gs[j]]<2) tmp=0; //如果數量不夠就讓 tmp=0 else tmp*=C[a[gs[j]]][2]*(b[gs[j]]?4:1); //否則加貢獻 elseif(a[gs[j]]<1) tmp=0;else tmp*=C[a[gs[j]]][1]*(b[gs[j]]?2:1);cmax(ans,tmp*13);}cnt=0;fp(i,1,34) if(a[i]>=2) tp[++cnt]=C[a[i]][2]*(b[i]?4:1);if(cnt>=7){ //如果牌數大于等于 2 的不止 7 張就可以構成七對子 sort(tp+1,tp+1+cnt); ll tmp=1; //選出權最大的 7 種牌 fp(i,cnt-6,cnt) tmp*=tp[i]; //累乘貢獻 cmax(ans,tmp*7);}f[0][0][0][0][0]=1; //初始化邊界 fp(i,0,33) fp(j,0,4) for(Rg int k=0;k<3&&j+k<=4;++k){if(k>=1&&(i==9||i==18||i>=27)) break; //不合法開頭無法構成順子 for(Rg int l=0;l<3&&j+k+l<=4;++l){if(l>=1&&(i==9||i==18||i==27)) break;if(f[i][j][k][l][0]||f[i][j][k][l][1]) fp(u,k+l,a[i+1]){ll tmp=C[a[i+1]][u]*(b[i+1]?(1<<u):1); //計算貢獻 // 四種轉移 if(j+u<=4&&u-k-l<3) cmax(f[i+1][j+k][l][u-k-l][0],f[i][j][k][l][0]*tmp),cmax(f[i+1][j+k][l][u-k-l][1],f[i][j][k][l][1]*tmp);if(u-k-l-2>=0&&j+u-2<=4)cmax(f[i+1][j+k][l][u-k-l-2][1],f[i][j][k][l][0]*tmp);if(u-k-l-3>=0&&j+u-2<=4)cmax(f[i+1][j+k+1][l][u-k-l-3][0],f[i][j][k][l][0]*tmp),cmax(f[i+1][j+k+1][l][u-k-l-3][1],f[i][j][k][l][1]*tmp);if(u==4&&!k&&!l&&j<=3)cmax(f[i+1][j+1][0][0][0],f[i][j][k][l][0]*tmp),cmax(f[i+1][j+1][0][0][1],f[i][j][k][l][1]*tmp);}}}cmax(ans,f[34][4][0][0][1]),print(ans);} return Ot(),0; }轉載于:https://www.cnblogs.com/Judge/p/10749200.html
總結
以上是生活随笔為你收集整理的题解 P5301 【[GXOI/GZOI2019]宝牌一大堆】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新广告法涉及的敏感词列表
- 下一篇: Opencv 找轮廓并画出相应的矩形