luogu P1283 【平板塗色】
阿新 • • 發佈:2020-11-30
閒話
第一道完全靠自己想出的藍題,怎麼說也要寫篇題解紀念一下
思路
看完題面,使用DFS應該是很顯然了
但是不同於普通深搜的列舉,這道題有一個限制條件
那就是一個矩形只能在所有緊靠它上方的矩形塗色後,才能塗色
所以我在開始搜尋前做了預處理,處理出了每個矩形在塗色前有哪些矩形需要先塗好,搜尋時再去判斷
每個需要預塗的矩形是否已先塗好
另外,由於搜尋時我是一次性將所有可以塗色的塗完,所以需要先對所有矩形按照從上到下的順序進行排序,這樣就可以保證在一次性的塗色中,當我要塗下方的矩形時,上方的同顏色矩形已經塗完了
之後的事情就非常簡單,只要照著DFS的模板打就行了
沒有剪枝好像由於資料範圍過小水過了
AC程式碼如下(帶很詳細的註釋)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int n,minn=1e9,tot,sum[17],ss[17][17],ans[1000],v;sum[i]代表第i個矩形上方緊靠著的矩形數 struct node{ //ss[i][j]代表第i個矩形的第j個緊靠著的矩形是哪一個 int x1,y1,x2,y2; int d; }s[17];//s[i].x1和y1代表第i個矩形左上方點的座標,s[i].x2和y2代表右下方點的座標,s[i].d代表要塗的顏色 bool ex[17]; void dfs(int k,int su)//k為拿起刷子的次數,su為已經塗色的矩形數目 { if(su==n) { minn=min(minn,k); return; } for(int g=1;g<=tot;g++)//tot為顏色總數 { int f=su,lin[17],ff=0;//lin[i]為臨時陣列,用來記錄新塗了哪些矩形,方便之後回溯 for(int i=1;i<=16;i++)lin[i]=0; for(int i=1;i<=n;i++) { if(s[i].d==g&&ex[i]==0) { int si=0; for(int j=1;j<=sum[i];j++)//判斷需要的矩形有幾個已經預先塗好 if(ex[ss[i][j]])si++; if(si==sum[i])//如果都塗好了就說明可以塗這個矩形了 { ex[i]=1;lin[++ff]=i;//標記為已塗過 su++; } } } if(f==su)continue;//如果用這個顏色不能塗更多矩形,就換一個顏色 ans[++v]=g;//記錄每一次使用的顏色 if(ans[v-1]!=ans[v]&&v!=1)//如果與上一次使用的顏色不同,就將次數加1 dfs(k+1,su); if(ans[v-1]==ans[v]||v==1)//如果與上一次使用的顏色相同或者是第一次使用,就不加次數 dfs(k,su); ans[v]=0;--v;//回溯 for(int i=1;i<=ff;i++) ex[lin[i]]=0,su--; } } bool cmp(node a,node b) { if(a.x1==b.x1)return a.y1<b.y1; return a.x1<b.x1; } int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>s[i].x1>>s[i].y1>>s[i].x2>>s[i].y2>>s[i].d; tot=max(tot,s[i].d);//更新tot } sort(s+1,s+n+1,cmp);//從上到下排序 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i!=j) { //如果其他矩形的y1與y2有一個在此矩形的y1與y2之間,並且是緊靠著的,就是合法的 if(((s[j].y1>=s[i].y1&&s[j].y1<s[i].y2)||(s[j].y2>s[i].y1&&s[j].y2<=s[i].y2))&&s[j].x1<s[i].x1&&s[i].x1==s[j].x2) { sum[i]++;//個數加1 ss[i][sum[i]]=j;//記錄是哪個矩形 } } } dfs(1,0); printf("%d",minn); return 0; }