洛谷P1283 平板塗色 &&一本通1445:平板塗色

題目描述

CE數碼公司開發了一種名爲自動塗色機(APM)的產品。它能用預約的顏色給一塊由不一樣尺寸且互不覆蓋的矩形構成的平板塗色。ios

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

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

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

【輸入】

第一行爲矩形的個數N。下面有N行描述了N個矩形。每一個矩形有5個整數描述,左上角的y座標和x座標,右下角的y座標和x座標,以及預約顏色。
顏色號爲1到20的整數。
平板的左上角座標老是(0, 0)。
座標的範圍是0..99。N小於16。優化

【輸出】

拿起刷子的最少次數。spa

【輸入樣例】

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 2code

【輸出樣例】

3
蒟蒻看到這題,先是mengbi,表示不會DP,只能搜索(+剪枝)
這數據居然過了(n<16)blog

搜索思路

讀入數據,統計顏色,而後每一個顏色都試一遍,即把該顏色的且能塗的磚塗上。
下一次塗色不能塗上次塗過的色。塗完了記錄結果排序

這不會超時嗎老鐵

爲了避免超時,加了兩個剪枝

最優化剪枝:當前塗色次數大於等於當前答案,直接退出(這個好理解吧)

可行性剪枝:若是當前一個磚都沒有塗到,直接退出(若是接着搜,會多一個次數,可能還會死循環,,,)

至於判斷該磚是否能塗,先預處理,把緊鄰該磚上方的磚用數組記錄下來,再判斷那些磚是否被塗ip

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#define ll long long
using namespace std;
inline int read()
{
   int s=0,w=1;
   char ch=getchar();
   while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
   while(isdigit(ch)) s=s*10+ch-'0',ch=getchar();
   return s*w;
}
struct lbq  //結構體 a1b1 該磚左上角座標 a2b2 右下角座標 x 顏色
{
    int a1,b1,a2,b2,x;
}a[20];
int ccmp(lbq a,lbq 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,ccmp);//按左上角座標大小從小到大排序(先考慮縱,再考慮橫)
    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;
}
相關文章
相關標籤/搜索