第1部分 基礎算法(提高篇)--第3章 深搜的剪枝技巧1445:平板塗色

1445:平板塗色

時間限制: 1000 ms 內存限制: 65536 KB
提交數: 565 通過數: 296
【題目描述】
CE數碼公司開發了一種名爲自動塗色機(APM)的產品。它能用預定的顏色給一塊由不同尺寸且互不覆蓋的矩形構成的平板塗色。

爲了塗色,APM需要使用一組刷子。每個刷子塗一種不同的顏色C。APM拿起一把有顏色C的刷子,並給所有顏色爲C且符合下面限制的矩形塗色:

在這裏插入圖片描述

爲了避免顏料滲漏使顏色混合,一個矩形只能在所有緊靠它上方的矩形塗色後,才能塗色。例如圖中矩形F必須在C和D塗色後才能塗色。注意,每一個矩形必須立刻塗滿,不能只塗一部分。

寫一個程序求一個使APM拿起刷子次數最少的塗色方案。注意,如果一把刷子被拿起超過一次,則每一次都必須記入總數中。

【輸入】
第一行爲矩形的個數N。下面有N行描述了N個矩形。每個矩形有5個整數描述,左上角的y座標和x座標,右下角的y座標和x座標,以及預定顏色。

顏色號爲1到20的整數。

平板的左上角座標總是(0, 0)。

座標的範圍是0…99。N小於16。

【輸出】
拿起刷子的最少次數。

【輸入樣例】
7
0 0 2 2 1
0 2 1 6 2
2 0 4 2 1
1 2 4 4 2
1 4 3 6 1
4 0 6 4 1
3 4 6 6 2
【輸出樣例】
3


思路:優化讀入數據,統計顏色,然後每個顏色都試一遍,即把該顏色的且能塗的磚塗上。下一次塗色不能塗上次塗過的色。塗完了記錄結果。
爲了不超時,加了兩個剪枝:最優化剪枝:當前塗色次數大於等於當前答案,直接退出。
可行性剪枝:如果當前一個磚都沒有塗到,直接退出,如果接着搜,會多一個次數,可能還會死循環。
至於判斷該磚是否能塗,先預處理,把緊鄰該磚上方的磚用數組記錄下來,再判斷那些磚是否被塗。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()//優化讀入數據
{
   int s=0,f=1;
   char ch=getchar();
   while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
   while(isdigit(ch)) s=s*10+ch-'0',ch=getchar();
   return s*f;
}
struct node  //結構體 a1,b1 該磚左上角座標, a2,b2 右下角座標, x 顏色
{
    int a1,b1,a2,b2,x;
}a[20];
int cmp(node a,node b)
{
    if(a.a1!=b.a1) return a.a1<b.a1;
    return a.b1<b.b1;
}
bool d=false;
int de[20];//de數組表示是否有該顏色
int n,m,ans=999,b[20],fk[20][20]; //b數組代表該磚是否被塗
bool OK(int o)
{
    for(int i=1;i<=n;i++)
        if(fk[o][i]&&!b[i]) return false; //如果i磚下面緊鄰o,但i沒塗過,返回false
    return true;
}
void dfs(int o,int pq,int xx)//o 塗色次數 pq 塗過顏色的磚 xx 上次塗的顏色
{
    if(o>=ans) return;//當前塗色次數大於等於當前答案,直接退出
    if(pq==n)//塗完了,記錄答案
    {
        ans=o;
        return;
    }
    for(int i=1;i<=m;i++)//枚舉顏色
    {
        int oj=0;//代表現在用這個顏色塗的磚數
        if(i!=xx&&de[i])//如果有這個顏色,並且這種顏色上次沒用過
        {
            for(int j=1;j<=n;j++) //塗色
                if(!b[j]&&a[j].x==i&&OK(j))
				//如果沒塗過該磚,並且能塗
                    b[j]=1,oj++;
                else
				if(b[j]&&a[j].x==i)
				b[j]++;
           if(oj>0)
		   dfs(o+1,pq+oj,i);//如果塗了磚,進行下一步
           for(int j=n;j>=1;j--)//回溯
                if(b[j]==1&&a[j].x==i&&OK(j))
                    b[j]=0,oj--;
                else
				if(b[j]>1&&a[j].x==i)
				b[j]--; 
        }
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i].a1=read(),a[i].b1=read(),a[i].a2=read(),a[i].b2=read(),a[i].x=read(),
        a[i].a1++,a[i].b1++,de[a[i].x]++;//記錄顏色
    for(int i=1;i<=20;i++)
	if(de[i])
	m=i; //求最大顏色編號
    sort(a+1,a+n+1,cmp);//按左上角座標大小從小到大排序(先縱,再橫)
    for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--)//fk[i][j]表示第i個磚是否緊鄰上方第j個磚
            if(a[i].a1==a[j].a2+1&&((a[i].b1>=a[j].b1&&a[i].b1<=a[j].b2)||(a[i].b2>=a[j].b1&&a[i].b2<=a[j].b2)))
                fk[i][j]=1;//如果i磚的最上面緊鄰j磚最下面,且兩磚橫座標有重疊,即j磚爲i磚緊鄰上面的磚
    dfs(0,0,0);
    printf("%d",ans);//答案
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章