給定一張n個點m條邊的帶權有向圖,每條邊的邊權只可能是1,2,3中的一種。將所有可能的路徑按路徑長度排序,請輸出第k小的路徑的長度,注意路徑不一定是簡單路徑,即可以重複走同一個點。
死毒瘤題,打了一晚上,最後把自己的方法改的和其他人差不多了
邊權不爲,不好直接套用鄰接矩陣,考慮把一個點拆開,讓一條長度大於的路徑要到一些沒用的點使得它需要走多次才能走到它該到的點
把一個點拆爲,分別表示距離還有的距離,實際上,就是原本的點,我們稱之爲實點,則是用來消耗路徑長度的點,稱之爲虛點
先有連邊
對一條邊,考慮由實點走出去一步,那麼距離就會減一,所以向連一條邊,即連邊
現在我們可以對現在的矩陣做乘法和快速冪了,每個實點到實點的權值就是方案數
如現在是這個矩陣的次方,則矩陣上上的權值表示的就是從出發,走步走到的方案數
接下來考慮怎麼求答案,很大,自然地就想到了倍增,類似求一樣的去確定答案即可
當然,直接對矩陣求次方,那麼求出來的矩陣裏的值表示的是剛好走步從某個位置走到某個位置的答案
爲了方便的進行判斷,我們需要把矩陣的值表示成走小於等於步的方案數
所以要考慮把走過的答案存下來,我們可以用號點表示所有的方案數
怎麼用號點表示呢,考慮每次算出一個值後再算下一個值時將當前的答案放到號點去
所以對每個點實點連一條的邊即可,而每次計算不能把上次的答案丟了,所以還要連一條的邊
這樣當前矩陣的號點就表示着上一次的答案
我們再弄一個矩陣乘上當前矩陣就可以表示,這個矩陣向所有的實點連一條邊即可
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年11月06日 星期三 18時58分03秒
*******************************/
#include <cstdio>
#include <fstream>
#define ll long long
#define ld long double
#define rint register int
using namespace std;
const int maxn = 130;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n,lim,m;
ll k,ans;
//{{{Matrix
struct Matrix{
ld mat[maxn][maxn];
Matrix (bool opt=0){
for (int i=0;i<=lim;++i)
for (int j=0;j<=lim;++j) mat[i][j]=opt&&(i==j);
}
ld* operator [] (const int &x){ return mat[x];}
Matrix operator * (Matrix &b){
Matrix a=*this,s;
for (int i=0;i<=lim;++i)
for (int j=0;j<=lim;++j)
for (int k=0;k<=lim;++k)
s[i][j]+=a[i][k]*b[k][j];
return s;
}
}g[maxn],s;
//}}}
inline int loc (int x,int i){ return (x-1)*3+i+1;}
inline ld sum (Matrix &a) { return a[0][0]-n; }
int main()
{
freopen("p3597.in","r",stdin);
freopen("p3597.out","w",stdout);
cin>>n>>m>>k;
lim=3*n;
for (rint i=1;i<=n;++i){
for (rint j=1;j<=2;++j) g[0][loc(i,j)][loc(i,j-1)]=1;
g[0][loc(i,0)][0]=s[0][loc(i,0)]=1;
}
g[0][0][0]=1;
for (rint i=1;i<=m;++i){
int u,v,d;
cin>>u>>v>>d;
++g[0][loc(u,0)][loc(v,d-1)];
}
int d;
for (d=1;;++d){
g[d]=g[d-1]*g[d-1];
Matrix t=s*g[d];
if (sum(t)>=k) break;
if (d>=64) return printf("-1\n"),0;
}
for (rint i=d;~i;--i){
Matrix t=s*g[i];
if (sum(t)<k){
s=t;
ans+=(1ll<<i);
}
}
printf("%lld\n",ans);
return 0;
}
如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧