題解:
這題不分在最短路里面,我以爲是個數學題呢,最後是個圖論題。轉自大佬的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;
}