HDU - 4370 0 or 1

題解:

這題不分在最短路里面,我以爲是個數學題呢,最後是個圖論題。轉自大佬的http://www.cnblogs.com/kuangbin/archive/2012/08/17/2644557.html

顯然,題目給的是一個0/1規劃模型。

解題的關鍵在於如何看出這個模型的本質。

3個條件明顯在刻畫未知數之間的關係,從圖論的角度思考問題,容易得到下面3個結論:

1.X12+X13+...X1n=1 於是1號節點的出度爲1

2..X1n+X2n+...Xn-1n=1 於是n號節點的入度爲1

3.∑Xki =∑Xij 於是2~n-1號節點的入度必須等於出度

於是3個條件等價於一條從1號節點到n號節點的路徑,故Xij=1表示需要經過邊(i,j),代價爲Cij。Xij=0表示不經過邊(i,j)。注意到Cij非負且題目要求總代價最小,因此最優答案的路徑一定可以對應一條簡單路徑。

最終,我們直接讀入邊權的鄰接矩陣,跑一次1到n的最短路即可,記最短路爲path。

以上情況設爲A

非常非常非常非常非常非常非常非常抱歉,簡單路徑只是充分條件,但不必要。(對造成困擾的隊伍深表歉意)

漏了如下的情況B:

從1出發,走一個環(至少經過1個點,即不能是自環),回到1;從n出發,走一個環(同理),回到n。

容易驗證,這是符合題目條件的。且A || B爲該題要求的充要條件。

由於邊權非負,於是兩個環對應着兩個簡單環。

因此我們可以從1出發,找一個最小花費環,記代價爲c1,再從n出發,找一個最小花費環,記代價爲c2。(只需在最短路算法更新權值時多加一條記錄即可:if(i==S) cir=min(cir,dis[u]+g[u][i]))

故最終答案爲min(path,c1+c2)

/*
HDU 4370 0 or 1
轉換思維的題啊,由一道讓人不知如何下手的題,轉換爲了最短路
基本思路就是把矩陣看做一個圖,圖中有n個點,1號點出度爲1,
n號點入度爲1,其它點出度和入度相等,路徑長度都是非負數,

等價於一條從1號節點到n號節點的路徑,故Xij=1表示需要經
過邊(i,j),代價爲Cij。Xij=0表示不經過邊(i,j)。注意到Cij非負
且題目要求總代價最小,因此最優答案的路徑一定可以對應一條簡單路徑。

最終,我們直接讀入邊權的鄰接矩陣,跑一次1到n的最短路即可,記最短路爲path。

漏了如下的情況B:
從1出發,走一個環(至少經過1個點,即不能
是自環),回到1;從n出發,走一個環(同理),回到n。
也就是1和n點的出度和入度都爲1,其它點的出度和入度爲0.

由於邊權非負,於是兩個環對應着兩個簡單環。

因此我們可以從1出發,找一個最小花費環,記代價爲c1,
再從n出發,找一個最小花費環,記代價爲c2。
(只需在最短路算法更新權值時多加一條記錄即可:if(i==S) cir=min(cir,dis[u]+g[u][i]))

故最終答案爲min(path,c1+c2)
*/
/*
本程序用SPFA來完成最短路。
但是由於要計算從出發點出發的閉環的路徑長度。
所以要在普通SPFA的基礎上做點變化。

就是把dist[start]設爲INF。同時一開始並不是讓出發點入隊,而是讓
出發點能夠到達的點入隊。
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int MAXN=330;
int cost[MAXN][MAXN];//保存路徑長度的鄰接矩陣
int dist[MAXN];
int que[MAXN];//注意隊列的循環利用,建成循環隊列
bool vis[MAXN];//是否在隊列中標記

void SPFA(int start,int n)
{
    int front=0,rear=0;
    for(int v=1;v<=n;v++)//初始化
    {
        if(v==start)//由於要找start的閉環,所以dist[start]設爲INF,且不入隊
        {
            dist[v]=INF;
            vis[v]=false;
        }
        else if(cost[start][v]!=INF)
        {
            dist[v]=cost[start][v];
            que[rear++]=v;
            vis[v]=true;
        }
        else//即dist[start][v]==INF情況,對本題沒有這種情況
        {
            dist[v]=INF;
            vis[v]=false;
        }
    }

    while(front!=rear)//注意這個條件是不等,因爲是循環隊列
    {
        int u=que[front++];
        for(int v=1;v<=n;v++)
        {
            if(dist[v]>dist[u]+cost[u][v])
            {
                dist[v]=dist[u]+cost[u][v];
                if(!vis[v])//不在隊列
                {
                    vis[v]=true;
                    que[rear++]=v;
                    if(rear>=MAXN) rear=0;//循環隊列
                }
            }
        }
        vis[u]=false;
        if(front>=MAXN)front=0;
    }

}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
            scanf("%d",&cost[i][j]);
        SPFA(1,n);
        int ans=dist[n];//1到n的最短路
        int loop1=dist[1];//1的閉環長度
        SPFA(n,n);
        int loopn=dist[n];//n的閉環長度
        ans=min(ans,loop1+loopn);
        printf("%d\n",ans);
    }
    return 0;
}

/*
用堆棧實現SPFA,有時候比隊列快
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=330;
const int INF=0x3f3f3f3f;

int cost[MAXN][MAXN];
int dist[MAXN];
int Q[MAXN];
bool vis[MAXN];

void SPFA(int start,int n)
{//堆棧實現,有時候比隊列快
    int top=0;
    for(int v=1;v<=n;v++)
    {
        if(v==start)
        {
            dist[v]=INF;
            vis[v]=false;
        }
        else
        {
            dist[v]=cost[start][v];
            vis[v]=true;
            Q[top++]=v;
        }
    }
    while(top!=0)
    {
        int u=Q[--top];
        for(int v=1;v<=n;v++)
        {
            if(dist[v]>dist[u]+cost[u][v])
            {
                dist[v]=dist[u]+cost[u][v];
                if(!vis[v])
                {
                    vis[v]=true;
                    Q[top++]=v;
                }
            }
        }
        vis[u]=false;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
            scanf("%d",&cost[i][j]);
        SPFA(1,n);
        int ans=dist[n];//1到n的最短路
        int loop1=dist[1];//1的閉環長度
        SPFA(n,n);
        int loopn=dist[n];//n的閉環長度
        ans=min(ans,loop1+loopn);
        printf("%d\n",ans);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章