1. 程式人生 > >2010年NOIP全國聯賽提高組 T4 引水入城

2010年NOIP全國聯賽提高組 T4 引水入城

題目中有圖片,建議大家去原處看圖之後再來看題解……

題解:
看完題目後,首先想什麼情況下無解,那不如先假設第一排全部建設蓄水廠,然後搜尋一下有沒有城市喝不到水,這樣複雜度是O(地圖大小QAQ)的,500*500綽綽有餘(記憶化的前提下)。

for(int i = 1;i <= m;i ++)  if(!vis[1][i])  bfs1(1,i);
bool flag = 0;
int cnt = 0;
for(int i = 1;i <= m;i ++)
{
    if(!vis[n][i])
    {
        flag = 1;
        cnt ++;
    }
}
if
(flag){printf("0\n%d",cnt); return 0;}

經過上面的判斷之後,如果所有城市都能喝上水,這說明題目一定有解,那麼下面對有解的情況進行分析:
如果題目有解,那麼建蓄水廠的地方一定能覆蓋一個區間,一定!
你可能回擔心下面這種情況:

/*
10 1 1 12 1 1
9  1 5 4  3 1
8  7 6 11 2 1
*/

但是仔細分析會發現上面情況就是無解的,12覆蓋的區間不如10覆蓋的區間多。
於是我們大概可以確定一個蓄水廠必須覆蓋一段連續的區間。
那麼我們的目的轉化為讓l儘可能小,r儘可能大,那麼如何實現呢?
我們用爬山的辦法來轉換,剛才是從上往下灌水,現在就從底層爬山,當處理最小的l的時候就從底層1往m依次搜尋,由於搜尋順序從1~m,所以左邊的一定比右邊的優(如果能搜到的話),那麼就可以加記憶化了:一個點只搜一遍,搜到頂層的某個點的時候吧某個點的l設定為當前搜尋的底層點的縱座標(點是第一層的所有點,不用開二維),r的時候恰好反過來……,暈了吧……看樣例:

/*
2 5
9 1 5 4 3
8 7 6 1 2
*/

8向上爬山爬到9,9在第一層,所以現在9的左端點就變成了1;
7向上爬山爬到8,由8再爬到9,但是我們記憶化過,所以上面過程在爬到8的時候已經停止了。
6向上爬到7,由於記憶化還是停止了……
……
……
2向上爬山爬到3,但是3在1爬山時已經走過,或者說2已經被走過,所以2不搜。

右端點同理:
2爬山一直爬到了9,直接更改9的座標和路徑上第一行5,4,3的座標,然後之後的點大致都不搜了。

處理完這些後每個點的l和r就是一個線段,最少蓄水廠就是最少線段覆蓋整個區間問題,這個問題可以DP,也可以貪心,這裡我選擇貪心(DP也不難……):
首先雙關鍵字排序:l和r都儘可能小。
now表示當前右端點,to表示能擴充套件的最遠點,ans表示需要的線段數(即蓄水廠數),然後從1號邊開始,直到一條線段的左端點大於當前右端點時,就擴充套件到to點,即最遠點,能擴充套件的最遠點再更新為當前線段的to,直到所有邊遍歷結束

但是有可能不能覆蓋所有部分,所以最後要加特判:

if(now != m)    ans ++;

至此 這個題就解決了,但是交上去90分?
天坑:n == 1。
……你一邊臨近湖一邊臨近沙漠什麼鬼啊……
這時候需要在bfs中加一些特判,即起點終點在一行的情況,加了特判後就ac了QAQ
下面詳見程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stack>
#define QAQ puts("ArchonOrz");
using namespace std;
const int size = 555;
const int INF = 233333;
int mps[size][size],n,m;
int step1[]={0,1,-1,0,0};
int step2[]={0,0,0,1,-1};
bool vis[size][size];
struct Poi{int x,y;};
queue <Poi> q;
void bfs1(int sx,int sy)
{
    q.push((Poi){sx,sy});
    vis[sx][sy] = 1;
    while(!q.empty())
    {
        Poi f = q.front();
        q.pop();
        for(int i = 1;i <= 4;i ++)
        {
            int x = f.x + step1[i];
            int y = f.y + step2[i];
            if(x < 1 || x > n || y < 1 || y > m || mps[x][y] >= mps[f.x][f.y] || vis[x][y]) continue;
            vis[x][y] = 1;
            q.push((Poi){x,y});
        }
    }
}
//bfs2 x -> l   y -> r 即控制區間 
Poi po[size];
void bfs2(int sx,int sy)
{
    if(sx == 1 && po[sy].x > sy)    po[sy].x = sy;
    q.push((Poi){sx,sy});
    vis[sx][sy] = 1;
    while(!q.empty())
    {
        Poi f = q.front();
        q.pop();
        for(int i = 1;i <= 4;i ++)
        {
            int x = f.x + step1[i];
            int y = f.y + step2[i];
            if(x < 1 || x > n || y < 1 || y > m || mps[x][y] <= mps[f.x][f.y] || vis[x][y]) continue;
            if(x == 1 && po[y].x > sy)  po[y].x = sy;
            vis[x][y] = 1;
            q.push((Poi){x,y});
        }
    }
}
void bfs3(int sx,int sy)
{
    if(sx == 1 && po[sy].y < sy)    po[sy].y = sy;
    q.push((Poi){sx,sy});
    vis[sx][sy] = 1;
    while(!q.empty())
    {
        Poi f = q.front();
        q.pop();
        for(int i = 1;i <= 4;i ++)
        {
            int x = f.x + step1[i];
            int y = f.y + step2[i];
            if(x < 1 || x > n || y < 1 || y > m || mps[x][y] <= mps[f.x][f.y] || vis[x][y]) continue;
            if(x == 1 && po[y].y < sy)  po[y].y = sy;
            vis[x][y] = 1;
            q.push((Poi){x,y});
        }
    }
}
struct Edge{int from,to;}edges[size];
int head[size],next[size],tot;
bool cmp(Edge a,Edge b)
{
    return a.from == b.from ? a.to < b.to : a.from < b.from;
}
void build(int f,int t)
{
    edges[++tot].to = t;
    edges[tot].from = f;
    next[tot] = head[f];
    head[f] = tot;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++)
        for(int j = 1;j <= m;j ++)
            scanf("%d",&mps[i][j]);
    for(int i = 1;i <= m;i ++)  if(!vis[1][i])  bfs1(1,i);
    bool flag = 0;
    int cnt = 0;
    for(int i = 1;i <= m;i ++)
    {
        if(!vis[n][i])
        {
            flag = 1;
            cnt ++;
        }
    }
    if(flag){printf("0\n%d",cnt);   return 0;}
    memset(vis,0,sizeof(vis));
//  QAQ
    for(int i = 1;i <= m;i ++)  po[i].x = INF,po[i].y = -INF;
    for(int i = 1;i <= m;i ++)  if(!vis[n][i])  bfs2(n,i);
    memset(vis,0,sizeof(vis));
//  QAQ
    for(int i = m;i > 0;i --)   if(!vis[n][i])  bfs3(n,i);
//  QAQ
//  for(int i = 1;i <= m;i ++)  printf("%d %d\n",po[i].x,po[i].y);
    for(int i = 1;i <= m;i ++)  if(po[i].x != INF && po[i].y != -INF)   build(po[i].x,po[i].y);
//  for(int i = 1;i <= tot;i ++)cout<<edges[i].from<<" "<<edges[i].to<<endl;
    sort(edges+1,edges+1+tot,cmp);
    int now = 0,ans = 0,to = 0;
    for(int i = 1;i <= tot;i ++)
    {
        if(now+1 >= edges[i].from)  to = max(to,edges[i].to);
        else now = to,to = max(to,edges[i].to),ans ++;
    }
    if(now != m)    ans ++;
    printf("1\n%d\n",ans);
    system("pause");
    return 0;
}

蒟蒻Orz各位大神