OIBH的NOIP模擬試題 巧置擋板

題面

題意

貓貓送走了客人,留住了蝸牛點點,晃晃悠悠走到池子邊,又打起了魚的主意。俯瞰池子,貓貓發現魚陣明顯亂了。“難道魚們故意讓我無法下嘴?”貓貓看到正在池邊散步的點點,心想:“一定是他告的密……”貓貓憤怒地抓起點點,把他關進了抽屜裏。貓貓想:“好啊,魚兒啊,你們不就是會傳遞信息嗎?有什麼了不起?用擋板把你們隔離開,看你們還怎麼交流!”貓貓隨即從後花園裏拿來若干擋板,打算用這些擋板在水中設立“隔離室”,把魚們分開。池塘已經被貓貓抽象成了n 行m列小方格,每個小方格中或有魚,或無魚。擋板必 須沿着小方格的邊緣放置。貓貓需要用擋板圍出若干個“隔離室”,使得每個隔離室中有且只有一條魚,而且,每個“隔離室”都必須爲矩形(包括正方形)。那麼,將魚們隔離開來,貓貓最少需要多少個長度單位的木板呢?
例如:
n=4 ,m=3 時的一種情況(0 表示小方格內無魚,1 表示有魚)。
0 1 0
0 0 0
1 0 1
0 0 0
可以這麼分:
這裏寫圖片描述
由於池塘邊框不需要放置擋板,所以這個分割方案所需的擋板總長度爲5。
輸入格式
第一行有兩個整數n和m,描述池塘規模。接下來的n行,每行有m個數字(非“0”即“1”)。每兩個數字之間用空格隔開。
對於20% 的數據,有1≤n,m≤10
對於40% 的數據,有1≤n,m≤16
對於70% 的數據,有1≤n,m≤24
對於100%的數據,有1≤n,m≤32
輸出格式
對於每組輸入數據,輸出一行:一個數字,貓貓所需擋板的最短總長度。

簡單來說就是給出一個01矩陣,問至少放幾塊擋板(每塊擋板長度爲1)可以將其分爲若干塊矩形,使每塊矩形中有且只有一個1.

法一

此方法複雜度不對,一旦魚很多就會超時,但因數據水,可以過

搜索+剪枝。
逐行考慮,首先枚舉這一行哪些位置放擋板,因爲最後被分成了若干矩形,所以當且僅當這塊擋板上面與其相鄰的兩塊橫着的擋板(或者是邊框)都存在時,它才能隨便放,否則它是否放置由它上面這個位置是否放置擋板決定。另外在遞歸時記錄一下這行是否已經有1(剪枝掉這一行存在兩個1,且中間沒有放置擋板的情況)
當這行豎着的擋板考慮完後,考慮這一行下面是否放置橫着的擋板,可以根據豎着的擋板將這一行分成多塊,若這一塊已經含有1,且下一行的這部分也有1,則必須放置橫着的擋板(若不放將導致一個矩形中有2個1),若這一塊中還沒有1,則不能放置橫着的擋板,這樣就可以使搜索情況大大減少,如果這行已經是最後一行,則還要判斷一下是不是沒塊中都只有1個1。
在搜索時還要加入估價函數進行剪枝:可以發現,如果這一行有k個1,則至少要放置k-1個擋板,這一列上也同理,這樣可以預處理出當前行下的整個矩形至少要放置幾塊擋板,若此時答案加上估價函數已經大於等於ans,直接剪枝。
爲了較快得到較優解(爲了可以利用估價函數剪掉較多的枝),可以先搜不放的情況,再搜較放的情況。

代碼

#include<iostream>
#include<cstdio>
#include<vector>
#define INF 0x3f3f3f3f
#define N 40
using namespace std;

int m,n,qz[N][N],need[N],ha[N],zt[N][N],ans=INF;
bool mm[N][N],h[N][N],l[N][N];
vector<int>gb[N];

inline int ask(int u,int v,int p,int q)
{
    return qz[p][q]-qz[p][v-1]-qz[u-1][q]+qz[u-1][v-1];
}

void dfs(int u,int v,int sum,bool have);
void Dfs(int u,int v,int sum)
{
    if(sum+need[u+1]>=ans) return;
    if(v==gb[u].size()-1)
    {
        dfs(u+1,1,sum,0);
        return;
    }
    if(zt[u][v]==1)
    {
        int i,j,p,q;
        p=gb[u][v];
        q=gb[u][v+1]-1;
        for(i=p;i<=q;i++)
        {
            h[u][i]=0;
        }
        Dfs(u,v+1,sum);
        for(i=p;i<=q;i++)
        {
            sum++;
            h[u][i]=1;
        }
        Dfs(u,v+1,sum);
    }
    else Dfs(u,v+1,sum);
}

void dfs(int u,int v,int sum,bool have)
{
    if(sum+need[u+1]>=ans) return;
    if(mm[u][v]&&have) return;
    have|=mm[u][v];
    if(v==n)
    {
        int i,j,p,q,o;
        gb[u].clear();
        gb[u].push_back(1);
        for(i=1; i<n; i++) if(l[u][i]) gb[u].push_back(i+1);
        gb[u].push_back(n+1);
        if(u==m)
        {
            for(i=0; i<gb[u].size()-1; i++)
            {
                p=gb[u][i];
                q=gb[u][i+1]-1;
                for(j=u-1; !h[j][p]; j--);j++;
                if(ask(j,p,u,q)!=1) return;
            }
            ans=sum;
            return;
        }
        for(i=0; i<gb[u].size()-1; i++)
        {
            p=gb[u][i];
            q=gb[u][i+1]-1;
            for(j=u-1; !h[j][p]; j--);j++;
            zt[u][i]=ask(j,p,u,q);
            if(zt[u][i]&&ask(u+1,p,u+1,q)) zt[u][i]=2;
            if(!zt[u][i])
            {
                for(j=p;j<=q;j++)
                {
                    h[u][j]=0;
                }
            }
            else if(zt[u][i]==2)
            {
                for(j=p;j<=q;j++)
                {
                    sum++;
                    h[u][j]=1;
                }
            }
        }
        Dfs(u,0,sum);
        return;
    }
    if(!h[u-1][v] || !h[u-1][v+1])
    {
        l[u][v]=l[u-1][v];
        dfs(u,v+1,sum+l[u][v],l[u][v]?0:have);
    }
    else
    {
        l[u][v]=0;
        dfs(u,v+1,sum,have);
        l[u][v]=1;
        dfs(u,v+1,sum+1,0);
    }
}

int main()
{
    int i,j,t;
    cin>>m>>n;
    for(i=1; i<=m; i++)
    {
        for(j=1; j<=n; j++)
        {
            scanf("%d",&mm[i][j]);
        }
    }
    for(i=1; i<=m; i++)
    {
        for(j=1; j<=n; j++)
        {
            qz[i][j]=qz[i-1][j]+qz[i][j-1]-qz[i-1][j-1]+mm[i][j];
        }
    }
    for(i=m-1; i>=1; i--) ha[i]+=ha[i+1];
    for(i=1; i<=m; i++)
    {
        for(j=i;j<=m;j++)
        {
            t=ask(j,1,j,n)-1;
            need[i]+=max(t,0);
        }
        for(j=1;j<=n;j++)
        {
            t=ask(i,j,m,j)-1;
            need[i]+=max(t,0);
        }
    }
    for(i=1; i<=n; i++) h[0][i]=1;
    dfs(1,1,0,0);
    cout<<ans;
}

法二

我們可以按一定的順序枚舉矩形,使任何時候,枚舉的矩形拼起來可以組成一個階梯形:
階梯形
因此我們存狀態時就只要存儲這條邊界即可,而且可以發現,如果下面任何一塊紅色區域內沒有魚,則必然不存在合法解:
這裏寫圖片描述這裏寫圖片描述這裏寫圖片描述
可以以此來剪枝。
然後每次狀態轉移時枚舉矩形即可。

代碼

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#define ll long long
#define INF 0x3f3f3f3f
#define base 127
#define N 50
using namespace std;

int m,n,qz[N][N],ans;
bool mm[N][N];
vector<int>tmp;
map<ll,int>dp;

inline int ask(int u,int v,int p,int q)
{
    return qz[p][q]+qz[u][v]-qz[p][v]-qz[u][q];
}

inline ll hsh(vector<int>u)
{
    int i;
    ll res=0;
    for(i=0; i<n; i++)
    {
        res=res*base+u[i];
    }
    return res;
}

int dfs(vector<int>u)
{
    int i;
    for(i=0; i<n; i++) if(u[i]!=m) break;
    if(i==n) return 0;
    ll zt=hsh(u);
    if(dp.count(zt)) return dp[zt];
    for(i=0; i<n; i++)
    {
        if(i&&u[i]!=u[i-1] || !i&&u[i]!=m)
        {
            if(!ask(u[i],i,m,n)) return dp[zt]=INF;
        }
    }
    int j,k,l,ii,t,res=INF,p;
    vector<int>v;
    for(i=0; i<n; i=j)
    {
        for(j=i+1; j<n&&u[j]==u[i]; j++);
        for(k=i+1; k<=j; k++)
        {
            p=i?u[i-1]:m;
            for(l=u[i]+1; l<=p; l++)
            {
                t=ask(u[i],i,l,k);
                if(t==1)
                {
                    v=u;
                    for(ii=i; ii<k; ii++) v[ii]=l;
                    res=min(res,dfs(v)+k-i+l-u[i]);
                }
                else
                {
                    if(!t) continue;
                    break;
                }
            }
        }
    }
    return dp[zt]=res;
}

int main()
{
    int i,j,p;
    cin>>m>>n;
    for(i=1; i<=m; i++)
    {
        for(j=1; j<=n; j++)
        {
            scanf("%d",&mm[i][j]);
            qz[i][j]=qz[i-1][j]+qz[i][j-1]-qz[i-1][j-1]+mm[i][j];
        }
    }
    for(i=1; i<=n; i++) tmp.push_back(0);
    ans=dfs(tmp)-m-n;
    cout<<ans;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章