題目描述
每天早晨,FJ從家中穿過農場走到牛棚。農場由 N 塊農田組成,農田通過 M 條雙向道路連接,每條路有一定長度。FJ 的房子在 1 號田,牛棚在 N 號田。沒有兩塊田被多條道路連接,以適當的路徑順序總是能在農場任意一對田間行走。當FZ從一塊田走到另一塊時,總是以總路長最短的道路順序來走。
FJ 的牛呢,總是不安好心,決定干擾他每天早晨的計劃。它們在 M 條路的某一條上安放一疊稻草堆,使這條路的長度加倍。牛希望選擇一條路干擾使得FJ 從家到牛棚的路長增加最多。它們請你設計並告訴它們最大增量是多少。
輸入輸出格式
輸入格式:
第 1 行:兩個整數 N, M。
第 2 到 M+1 行:第 i+1 行包含三個整數 A_i, B_i, L_i,A_i 和 B_i 表示道路 i 連接的田的編號,L_i 表示路長。
輸出格式:
第 1 行:一個整數,表示通過使某條路加倍而得到的最大增量。
輸入輸出樣例
輸入樣例#1:
5 7
2 1 5
1 3 1
3 2 8
3 5 7
3 4 3
2 4 7
4 5 2
輸出樣例#1:
2
說明
【樣例說明】
若使 3 和 4 之間的道路長加倍,最短路將由 1-3-4-5 變爲 1-3-5。
【數據規模和約定】
對於 30%的數據,N <= 70,M <= 1,500。
對於 100%的數據,1 <= N <= 100,1 <= M <= 5,000,1 <= L_i <= 1,000,000。
題解:、
暴力做法:枚舉每條邊,將邊權*2,跑一遍dij最短路,更新答案。
正解:首先跑一遍dij求出最短路,那麼如果將除了該最短路上的路徑的其他邊翻倍的話,那麼再次dij時得到的一定還是第一遍求出的最短路。所以正解是枚舉最短路上的每一條邊,將其翻倍,然後跑一遍dij更新答案。
代碼:
#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,pre[25005],k,ans;
int minn,c[255],maxx=0x7fffffff,anss=-1;
int f[255][255];
bool flag[255],pd[255][255];
int get()
{
int x=0,p=1;
char c;
c=getchar();
while (c<'0'||c>'9') {if (c=='-') p=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return x*p;
}
int main()
{
int I,x,y,z,i,j;
n=get();m=get();
memset(f,127/3,sizeof(f));
for (i=1;i<=m;i++)
{
x=get();y=get();z=get();
f[x][y]=f[y][x]=z;
pd[x][y]=pd[y][x]=1;
}
memset(c,127/3,sizeof(c));
memset(flag,false,sizeof(flag));
c[1]=0;
for (i=1;i<=n;i++)
{
minn=maxx;
k=0;
for (j=1;j<=n;j++)
if ((!flag[j])&&(c[j]<minn))
{
minn=c[j];
k=j;
}
if (k==0) break;
flag[k]=true;
for (j=1;j<=n;j++)
{
if (c[k]+f[k][j]<c[j])
{
c[j]=c[k]+f[k][j];
pre[j]=k;
}
}
}
ans=c[n];
pre[1]=0;
for (I=n;pre[I];I=pre[I])
{
f[I][pre[I]]*=2;
f[pre[I]][I]*=2;
memset(c,127/3,sizeof(c));
memset(flag,false,sizeof(flag));
c[1]=0;
for (i=1;i<=n;i++)
{
minn=maxx;
k=0;
for (j=1;j<=n;j++)
if ((!flag[j])&&(c[j]<minn))
{
minn=c[j];
k=j;
}
if (k==0) break;
flag[k]=true;
for (j=1;j<=n;j++)
{
if (c[k]+f[k][j]<c[j])
c[j]=c[k]+f[k][j];
}
}
anss=max(anss,c[n]-ans);
f[I][pre[I]]/=2;
f[pre[I]][I]/=2;
}
printf("%d",anss);
return 0;
}