1. 程式人生 > >【NOIP2017模擬賽】思維+轉化+圖論 徒然Children(好題)

【NOIP2017模擬賽】思維+轉化+圖論 徒然Children(好題)

題目描述

全世界都在談戀愛,只有我深愛著學習。——前言。
2333身為單身狗卻意外喜歡的吃狗糧的yuki堅信著:“自古紅藍出cp,黑白天生是夫妻,最是銷魂紅綠配,天然金紫成雙對,生死相隨紅與黃,白紫一逢春滿堂,千里緣牽白與綠,紅黑生來為相聚。”的cp配對原則,卻被徒然children中綠黃的御宅的春天組(山根隆夫×慄原千代)莫名戳中萌點。可見死宅真是一個有前途的職業。在本季末尾,一群cp們正在舉行足球賽,想要偷懶的可愛死宅山根隆夫卻聽到了更可愛的慄原千代的加油聲,他感到很無奈,想要在操場上選擇一些不需要跑動的地方偷懶,又不想太過明顯影響在妹子心中的形象。操場是一個n*n方格矩陣,每一個格子上有一個非負整數A(i,j)表示山根隆夫停在這裡會在慄原千代心中減多少分,他希望在操場中選若干格子歇息,B(i,j)=1表示格子(i,j)被選中,B(i,j)=0表示格子(i,j)未被選中。矩陣B必須滿足三個條件山根隆夫才不會被紅牌下場。
B

(1,2)+B(1,3)+...B(1,n)=1
B(1,n)+B(2,n)+...B(n1,n)=1
ΣB(k,i)(1<=k<=n)=ΣB(i,j)(1<=j<=n)(1<i<n)
身為死宅的你,一定也有著和超級可愛的死宅山根隆夫一樣的關於妹子的理想。那麼請你幫幫他,找出一種方案使他在妹子心中減的分最少。

輸入

第一行一個整數n表示操場的大小,n行n列的格子。接下來n行每行n各整數表示A(i,j)。

輸出

一行一個整數表示山根隆夫在可愛的慄原千代心中最少減了多少分。

樣例輸入

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

樣例輸出

1
對於前10%的資料:n<=10
對於前20%的資料:n<=100
對於前50%的資料:n<=1000
對於100%的資料:n<=2000

題解

初看題面,你會覺得這道題很難,然後去思考各種各樣的數學方法,或者一些詭異的亂搞的方法。
其實,如果你仔細看這道題,它其實是一道圖論模板(驚歎ing)。
如果把A看作是鄰接矩陣,這時再看看B的條件就會發現一些鬼畜的事情。首先,B(1,2)+B(1,3)+...B(1,n)=1,就是說必須選中且僅選中一條從1到一個非1節點的邊。第二,B(1,n)+B(2,n)+...B(n1,n)=1,就是說必須選中且僅選中一個從非n

節點到n的邊。接下來第三個要求,ΣB(k,i)=ΣB(i,j),就是說2~n1的節點必須選了一條入邊就要選一條出邊。這樣一來,我們會發現有兩種可能滿足這些條件。第一種:一條從1>n的最短路;第二種:一個包含1的最小環+一個包含n的最小環。這樣一來,我們只需要寫一個SPFA,就可以同時解決兩個問題,從而得出最小值。
仍然是沒有註釋的程式碼,抱歉啦~

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 2005
#define ll long long
int n,vis[N];
ll A[N][N],dis[N],ans,rnd=1ll<<60;
queue<int>q;
ll getll()
{
    ll p=0;char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
        p=p*10+c-'0',c=getchar();
    return p;
}
void putll(ll a)
{
    if(a<0)putchar('-'),a=-a;
    if(a>9)putll(a/10);
    putchar(a%10+'0');
}
void SPFA(int s)
{
    for(int i=0;i<=n;i++)
        dis[i]=1ll<<60;
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=1;i<=n;i++)
        {
            if(dis[x]+A[x][i]<dis[i])
            {
                dis[i]=dis[x]+A[x][i];
                if(!vis[i])
                {
                    vis[i]=1;
                    q.push(i);
                }
            }
        }
        vis[x]=0;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            A[i][j]=getll();
    SPFA(1);
    ans=dis[n];
    for(int i=2;i<=n;i++)
        rnd=min(rnd,dis[i]+A[i][1]);
    SPFA(n);
    for(int i=1;i<n;i++)
        ans=min(ans,rnd+dis[i]+A[i][n]);
    putll(ans);
    putchar(10);
}