洛谷P4907【CYH-01】小奔的國慶練習賽 :$A$換$B$ $problem$(DFS,剪枝)
阿新 • • 發佈:2018-10-04
技巧 show -h 可能 reg http 復雜度 tex \n ,超過了\(10^8\),肯定需要剪枝。
洛谷題目傳送門
順便提一下題意有一個地方不太清楚,就是如果輸出No
還要輸出最少需要添加多少張牌才能滿足要求。蒟蒻考完以後發現四個點Too short on line 2
。。。
比較需要技巧的搜索
既然是同一個花色要連續,那就枚舉每一個花色在哪一段區間連續並選中四個區間,累計每個點數的選中次數。
最後來一個\(O(13)\)的\(\text{check}\),首先每個點數選中次數要不少於已有的個數。接著,只有所有點數的選中次數和已有點數相等時,才能判為‘Yes‘,然後統計某張牌的花色的區間未包含這張牌的總數更新答案。否則判為‘No‘,然後統計每個點數選多了的總數更新答案。
總復雜度\(\binom{13}{2}^4* 13=481195728\)
可行性剪枝:每個點數選中次數之和不少於原有牌的總數。
最優性剪枝:實時統計選多了的總數,在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; }
洛谷P4907【CYH-01】小奔的國慶練習賽 :$A$換$B$ $problem$(DFS,剪枝)