1. 程式人生 > >學了一年文化課後變成 白癡退役夕陽紅選手的 陳年老題信心%你賽 B題

學了一年文化課後變成 白癡退役夕陽紅選手的 陳年老題信心%你賽 B題

data con 細節 pan sizeof 最短路 樸素 很多 sub

唉。沒什麽話說。

首先我這個傻子考場上都知道,先把mmin求出來,再用mmin那些邊的節點去spfa延伸找其他點。

然而都是口胡。

看一下細節。男神先把全部的邊都減去mmin,這樣一來spfa的時候不用每次都增加,減少編程復雜度,然後spfa的時候也是一個我沒有想到的操作,就是他非常心安理得的第二維只開了2

一個樸素的想法是f[i][j]表示走到點i,有j條邊要使用後面的邊權的最短路。f[i][j]可以轉移到f[k][0]或f[k][j+1]。然而這樣邊數是O(n3)O(n3)的,spfa不資磁。。

但是考慮做最短路的目的是找到一個標記點,走到很多個中間點顯然不是什麽好方案。具體來說,從i走j條邊到k,只是為了使用最後一條邊權(s,k),那為什麽不直接i->s->k呢?所以f[i][j]中的j只需要取0,1。

ORZ

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL inf(1LL<<60);

int n;LL mmin;
LL mp[2100][2100];
struct list_
{
    int x,w;
}list[
4100];bool v[2100][2]; LL d[2100][2]; void spfa() { memset(d,63,sizeof(d)); memset(v,false,sizeof(v)); int head=1,tail=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(mp[i][j]==0&&i!=j) { d[i][0]=0; v[i][
0]=true; list[tail].x=i; list[tail].w=0; tail++; break; } while(head!=tail) { int x=list[head].x,w=list[head].w; for(int y=1;y<=n;y++) { if(x==y)continue; LL dis=d[x][w]+(w+1)*mp[x][y]; if(d[y][0]>dis) { d[y][0]=dis; if(v[y][0]==false) { v[y][0]=true; list[tail].x=y; list[tail].w=0; tail++;if(tail==4050)tail=1; } } if(w==0) { dis=d[x][w]; if(d[y][1]>d[x][w]) { d[y][1]=d[x][w]; if(v[y][1]==false) { v[y][1]=true; list[tail].x=y; list[tail].w=1; tail++;if(tail==4050)tail=1; } } } } v[x][w]=false; head++;if(head==4050)head=1; } } int main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout); scanf("%d",&n);mmin=inf; for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { scanf("%lld",&mp[i][j]); mp[j][i]=mp[i][j]; mmin=min(mmin,mp[i][j]); } } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) mp[i][j]-=mmin; spfa(); for(int i=1;i<=n;i++)printf("%lld\n",d[i][0]+(n-1)*mmin); return 0; }

學了一年文化課後變成 白癡退役夕陽紅選手的 陳年老題信心%你賽 B題