程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> bzoj 1001狼抓兔子(對偶圖+最短路)最大流,bzoj1001

bzoj 1001狼抓兔子(對偶圖+最短路)最大流,bzoj1001

編輯:C++入門知識

bzoj 1001狼抓兔子(對偶圖+最短路)最大流,bzoj1001


推薦文章:《淺析最大最小定理在信息學競賽中的應用》--周冬

題目

現在小朋友們最喜歡的"喜羊羊與灰太狼",話說灰太狼抓羊不到,但抓兔子還是比較在行的, 而且現在的兔子還比較笨,它們只有兩個窩,現在你做為狼王,面對下面這樣一個網格的地形: 左上角點為(1,1),右下角點為(N,M)(上圖中N=4,M=5).有以下三種類型的道路 1:(x,y)<==>(x+1,y) 2:(x,y)<==>(x,y+1) 3:(x,y)<==>(x+1,y+1) 道路上的權值表示這條路上最多能夠通過的兔子數,道路是無向的. 左上角和右下角為兔子的兩個窩, 開始時所有的兔子都聚集在左上角(1,1)的窩裡,現在它們要跑到右下解(N,M)的窩中去,狼王開始伏擊 這些兔子.當然為了保險起見,如果一條道路上最多通過的兔子數為K,狼王需要安排同樣數量的K只狼, 才能完全封鎖這條道路,你需要幫助狼王安排一個伏擊方案,使得在將兔子一網打盡的前提下,參與的 狼的數量要最小。因為狼還要去找喜羊羊麻煩. Input 第一行為N,M.表示網格的大小,N,M均小於等於1000. 接下來分三部分 第一部分共N行,每行M-1個數,表示橫向道路的權值. 第二部分共N-1行,每行M個數,表示縱向道路的權值. 第三部分共N-1行,每行M-1個數,表示斜向道路的權值. 輸入文件保證不超過10M Output 輸出一個整數,表示參與伏擊的狼的最小數量. Sample Input 3 4 5 6 4 4 3 1 7 5 3 5 6 7 8 8 7 6 5 5 5 5 6 6 6 Sample Output 14 View Code

題目圖片

 

講這部分之前,請先閱讀以上的文章(講得十分好%%%)(當然閱讀到27頁就好了)

讀完後,我們發現這道題完全可以用其對偶圖來跑最短路。

原圖                                 對偶圖

面數 x                              面數 y

點數 y   那麼其對偶圖中      點數 x

邊數 z                               邊數 z

面數和點數正好相反。

將原圖的起點和終點連接起來,建立一個新的面(這是必須的)。

 

當然s點和t點之間是沒有邊的。

s和1,7,9,11之間有邊。

t和2,4,6,12之間有邊。

上面是我們建好的對偶圖,從左至右依次編號,關於對偶圖中面的編號,因為我們是按照橫邊,縱邊,斜邊的順序讀入的,所以我們一定要按照一定的方法對這些圖編號

我采用的是從左至右依次編號,因為我們可以很清楚當前邊連接的兩個點(原圖中的兩個面)所在的位置,因為這是平面圖,所以可以用歐拉公式來求出之前有多少點(

原圖中的面)。再加上這個點(原圖中的面(重要的事說三遍))在當前行中的位置就是它的編號。

只要原圖中的兩個面之間存在邊,那麼它的對偶圖中的兩個點就存在邊。

 

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int cnt, i, j, x, xx, xxx, h, t, s, hh[2010000], n, m, w;
bool dd[2010000];
int e, l[2010000], d[1010000], hhh, ww;
struct node
{
    int v, next, z;
} b[6010000];
inline void add(int aa, int bb, int cc)//鄰接表,建雙向邊
{
    b[++cnt].v = bb;
    b[cnt].next = hh[aa];
    b[cnt].z = cc;
    hh[aa] = cnt;
    b[++cnt].v = aa;
    b[cnt].next = hh[bb];
    b[cnt].z = cc;
    hh[bb] = cnt;
}
void add1()
{
    for(i = 1; i < m; ++i)
    {
        scanf("%d", &x);
        add(i * 2, t, x);
    }
    for(i = 2; i < n; ++i)
    {
        for(j = 1; j < m; ++j)
        {
            scanf("%d", &x);
            add((i - 1) * (m - 1) * 2 + j * 2, (i - 1) * (m - 1) * 2 + j * 2 - m * 2 + 1, x);//利用歐拉公式確定編號並建邊(下面的add函數也是如此)
        }
    }
    for(j = 1; j < m; ++j)
    {
        scanf("%d", &x);
        add(s, (n - 2) * 2 * (m - 1) + j * 2 - 1, x);
    }
}
void add2()
{
    for(i = 1; i < n; ++i)
    {
        scanf("%d", &x);
        xx = (i - 1) * (m - 1) * 2 + 1;
        add(s, xx, x);
        for(j = 2; j < m; ++j)
        {
            scanf("%d", &x);
            xx += 2;
            add(xx - 1, xx, x);
        }
        scanf("%d", &x);
        add(xx + 1, t, x);
    }
}
inline void add3()
{

    for(i = 1; i < n; ++i)
    {
        for(j = 1; j < m; ++j)
        {
            scanf("%d", &x);
            add((i - 1) * (m - 1) * 2 + j * 2, (i - 1) * (m - 1) * 2 + j * 2 - 1, x);
        }
    }
}
//下面的spfa中一定要用循環隊列,省空間。不用的話空間開小(這很有可能畢竟1百萬個點)可能會被卡。 void spfa() { dd[s] = true; h = 0; w = 0; memset(l,0x3f,sizeof(l));//將l數組賦成最大值
//hhh記錄的是我們用的是隊列中第幾個元素(實際上)
//ww記錄的是隊列中總共有幾個元素 l[s] = 0; while(1) { if(hhh > ww)break; h = hhh % 1000001; w = ww % 1000001; for(i = hh[s]; i; i = b[i].next) { e = b[i].v; if(l[s] + b[i].z < l[e]) { l[e] = l[s] + b[i].z; if(!dd[e])w = ww % 1000000, d[++w] = e, ww++, dd[e] = true; } } dd[s] = false; h = hhh % 1000000; s = d[++h]; hhh++; } } int main() { scanf("%d %d", &n, &m); if(n == m && n == 1)//特判 { printf("0"); return 0; } s = 0; t = 2 * (n - 1) * (m - 1) + 1; add1();//讀入橫邊 add2();//讀入縱邊 add3();//讀入斜邊 spfa();//對其對偶圖求s————t的最短路 printf("%d", l[t]); return 0; }

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved