2013-1019
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
解题报告:
首先,该题有一个这样的性质,在保证最优的情况下,加速次数只能是0,1,2……w,或者是无穷次。
证明如下,对于两个加速点a,b,a为距b最近的加速点,假设最优方案要经过b的话。
ab间一次来回,需要花费(3/4)*(2*d(a,b))的时间,但是速度翻了4倍,于是b到终点时间减少了3/4。
所以
1、若ab距离的两倍小于等于b到终点最短距离,则在ab间无限加速更快。
时间收敛到ab距离的两倍除以b点在开始绕之前的速度,之后瞬间到终点(无限速度)
2、若ab距离的两倍大于b到终点的最短距离,则选择不绕,直接走向终点。
具体做法:
1、对于无穷的处理:首先我们对全图做一次floyd,加速点不作为中间节点。就能求出加速点之间的距离(普通点可以忽略)。
然后对于每一个加速点i,找到一个与他最近的加速点,距离存为d[i]。
2、分层图SPFA:将每个点按加速次数分成若干个点(加速0次,1次……w次),然后正向做一次SPFA,求出每个点在每个加速次数下的最小到达时间。
3、算出到终点的最小时间以及加速次数:对于每个加速点,判断一下是直接到终点快还是无限加速(2*d[i]/v)到终点快,在这个基础下再得出最大加速次数。
可以注意到,当加速次数超过57次的话,已经超越的double的精度,而SPFA中需要处理的加速次数最高为99次。
所以我们不能用double来存储最小时间,而是用3个long long来存储,这样精度就能达到小数点后120位,可以精确地算出到达每个点的时间。
}}}
{{{
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MN=101,MX=1000001;
long long e[64];
struct dg100 { long long p[3]; } t[MN][MN];
int f[MN][MN],a[MN],p[MN],d[MN];
int n,m,w;
//f->edge;
//a->acceleration point;
//p[i]->weather vertex i is acceleration point;
//d[i]->the shortest distance between two acceleration point;
//t[i][j]->the time to reach vertex i with j times acceleration;
//e[i]=2^i;
void init(){
int i,x,y,z;
memset(f,1,sizeof(f));
for (i=1; i<=m; i++){
scanf("%d%d%d",&x,&y,&z);
if (f[x][y]>z){ f[x][y]=z; f[y][x]=z; }
}
memset(p,0,sizeof(p));
memset(a,0,sizeof(a));
a[0]=w;
for (i=1; i<=a[0]; i++){
scanf("%d",&x);
p[x]=1; a[i]=x;
}
}
void floyd(){
int i,j,k;
for (k=1; k<=n; k++) if (!p[k])
for (i=1; i<=n; i++) if (i!=k)
for (j=i+1; j<=n; j++)
if (f[i][k]+f[k][j]<f[i][j]) {
f[i][j]=f[i][k]+f[k][j];
f[j][i]=f[i][j];
}
memset(d,1,sizeof(d));
for (i=1; i<=a[0]; i++)
for (j=1; j<=a[0]; j++) if (i!=j)
if (f[a[i]][a[j]]<d[a[i]]) d[a[i]]=f[a[i]][a[j]];
}
dg100 time(int a,int v){
dg100 k;
k.p[0]=0; k.p[1]=0; k.p[2]=0;
int i=v/60,j;
v=v%60;
k.p[i]=a/e[v];
k.p[i+1]=a%e[v]*e[60-v];
return k;
}
void add(dg100* a,dg100 x,dg100 y){
a->p[2]=x.p[2]+y.p[2];
a->p[1]=x.p[1]+y.p[1]+a->p[2]/e[60];
a->p[2]=a->p[2]%e[60];
a->p[0]=x.p[0]+y.p[0]+a->p[1]/e[60];
a->p[1]=a->p[1]%e[60];
}
int cmp(dg100 a,dg100 b){
int i;
for (i=0; i<3; i++)
if (a.p[i]<b.p[i]) return -1;
else if(a.p[i]>b.p[i]) return 1;
return 0;
}
int ass(dg100* a,dg100 b){
a->p[0]=b.p[0];
a->p[1]=b.p[1];
a->p[2]=b.p[2];
}
void dp(){
int i,j,k;
for (i=1; i<=n; i++)
for (j=0; j<=n; j++) t[i][j].p[0]=MX;
for (k=0; k<3; k++)
t[1][p[1]].p[k]=0;
dg100 temp;
int v=p[1],x=1,y;
for (j=1; j<=a[0]; j++){
y=a[j];
if (f[x][y]>=MX) continue;
add(&temp,t[x][v],time(f[x][y],v));
if (cmp(temp,t[y][v+1])==-1) ass(&t[y][v+1],temp);
}
for (v=p[1]+1; v<a[0]; v++)
for (i=1; i<=a[0]; i++){
x=a[i];
if (t[x][v].p[0]>=MX) continue;
for (j=1; j<=a[0]; j++){
y=a[j];
if (f[x][y]>=MX) continue;
add(&temp,t[x][v],time(f[x][y],v));
if (cmp(temp,t[y][v+1])==-1) ass(&t[y][v+1],temp);
}
}
}
void writeit(){
dg100 ans,temp;
ans.p[0]=f[1][n]; ans.p[1]=0; ans.p[2]=0;
int i,j,k,v=p[1],flag;
f[n][n]=0;
for (i=1; i<=a[0]; i++)
for (j=1; j<=a[0]; j++){
k=a[i];
add(&temp,t[k][j],time(f[k][n],j));
flag=cmp(temp,ans);
if (flag==-1 || flag==0 && j>v) { ass(&ans,temp); v=j; }
add(&temp,t[k][j],time(d[k]*2,j));
flag=cmp(temp,ans);
if (flag!=1) { ass(&ans,temp); v=MX; }
}
double a=ans.p[0],b=ans.p[1],c=ans.p[2];
b=b/e[60]; c=c/e[60]/e[60];
a=a+b+c;
printf("%.6f ",a);
if (v>=MX) printf("Burst Evolution!\n"); else printf("%d\n",v);
}
int main(){
e[0]=1;
for (int i=1; i<=61; i++) e[i]=e[i-1]*2;
while (scanf("%d%d%d",&n,&m,&w)!=EOF){
init();
floyd();
dp();
writeit();
}
return 0;
}
}}}
解题报告:
首先,该题有一个这样的性质,在保证最优的情况下,加速次数只能是0,1,2……w,或者是无穷次。
证明如下,对于两个加速点a,b,a为距b最近的加速点,假设最优方案要经过b的话。
ab间一次来回,需要花费(3/4)*(2*d(a,b))的时间,但是速度翻了4倍,于是b到终点时间减少了3/4。
所以
1、若ab距离的两倍小于等于b到终点最短距离,则在ab间无限加速更快。
时间收敛到ab距离的两倍除以b点在开始绕之前的速度,之后瞬间到终点(无限速度)
2、若ab距离的两倍大于b到终点的最短距离,则选择不绕,直接走向终点。
具体做法:
1、对于无穷的处理:首先我们对全图做一次floyd,加速点不作为中间节点。就能求出加速点之间的距离(普通点可以忽略)。
然后对于每一个加速点i,找到一个与他最近的加速点,距离存为d[i]。
2、分层图SPFA:将每个点按加速次数分成若干个点(加速0次,1次……w次),然后正向做一次SPFA,求出每个点在每个加速次数下的最小到达时间。
3、算出到终点的最小时间以及加速次数:对于每个加速点,判断一下是直接到终点快还是无限加速(2*d[i]/v)到终点快,在这个基础下再得出最大加速次数。
可以注意到,当加速次数超过57次的话,已经超越的double的精度,而SPFA中需要处理的加速次数最高为99次。
所以我们不能用double来存储最小时间,而是用3个long long来存储,这样精度就能达到小数点后120位,可以精确地算出到达每个点的时间。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MN=101,MX=1000001;
long long e[64];
struct dg100 { long long p[3]; } t[MN][MN];
int f[MN][MN],a[MN],p[MN],d[MN];
int n,m,w;
//f->edge;
//a->acceleration point;
//p[i]->weather vertex i is acceleration point;
//d[i]->the shortest distance between two acceleration point;
//t[i][j]->the time to reach vertex i with j times acceleration;
//e[i]=2^i;
void init(){
int i,x,y,z;
memset(f,1,sizeof(f));
for (i=1; i<=m; i++){
scanf("%d%d%d",&x,&y,&z);
if (f[x][y]>z){ f[x][y]=z; f[y][x]=z; }
}
memset(p,0,sizeof(p));
memset(a,0,sizeof(a));
a[0]=w;
for (i=1; i<=a[0]; i++){
scanf("%d",&x);
p[x]=1; a[i]=x;
}
}
void floyd(){
int i,j,k;
for (k=1; k<=n; k++) if (!p[k])
for (i=1; i<=n; i++) if (i!=k)
for (j=i+1; j<=n; j++)
if (f[i][k]+f[k][j]<f[i][j]) {
f[i][j]=f[i][k]+f[k][j];
f[j][i]=f[i][j];
}
memset(d,1,sizeof(d));
for (i=1; i<=a[0]; i++)
for (j=1; j<=a[0]; j++) if (i!=j)
if (f[a[i]][a[j]]<d[a[i]]) d[a[i]]=f[a[i]][a[j]];
}
dg100 time(int a,int v){
dg100 k;
k.p[0]=0; k.p[1]=0; k.p[2]=0;
int i=v/60,j;
v=v%60;
k.p[i]=a/e[v];
k.p[i+1]=a%e[v]*e[60-v];
return k;
}
void add(dg100* a,dg100 x,dg100 y){
a->p[2]=x.p[2]+y.p[2];
a->p[1]=x.p[1]+y.p[1]+a->p[2]/e[60];
a->p[2]=a->p[2]%e[60];
a->p[0]=x.p[0]+y.p[0]+a->p[1]/e[60];
a->p[1]=a->p[1]%e[60];
}
int cmp(dg100 a,dg100 b){
int i;
for (i=0; i<3; i++)
if (a.p[i]<b.p[i]) return -1;
else if(a.p[i]>b.p[i]) return 1;
return 0;
}
int ass(dg100* a,dg100 b){
a->p[0]=b.p[0];
a->p[1]=b.p[1];
a->p[2]=b.p[2];
}
void dp(){
int i,j,k;
for (i=1; i<=n; i++)
for (j=0; j<=n; j++) t[i][j].p[0]=MX;
for (k=0; k<3; k++)
t[1][p[1]].p[k]=0;
dg100 temp;
int v=p[1],x=1,y;
for (j=1; j<=a[0]; j++){
y=a[j];
if (f[x][y]>=MX) continue;
add(&temp,t[x][v],time(f[x][y],v));
if (cmp(temp,t[y][v+1])==-1) ass(&t[y][v+1],temp);
}
for (v=p[1]+1; v<a[0]; v++)
for (i=1; i<=a[0]; i++){
x=a[i];
if (t[x][v].p[0]>=MX) continue;
for (j=1; j<=a[0]; j++){
y=a[j];
if (f[x][y]>=MX) continue;
add(&temp,t[x][v],time(f[x][y],v));
if (cmp(temp,t[y][v+1])==-1) ass(&t[y][v+1],temp);
}
}
}
void writeit(){
dg100 ans,temp;
ans.p[0]=f[1][n]; ans.p[1]=0; ans.p[2]=0;
int i,j,k,v=p[1],flag;
f[n][n]=0;
for (i=1; i<=a[0]; i++)
for (j=1; j<=a[0]; j++){
k=a[i];
add(&temp,t[k][j],time(f[k][n],j));
flag=cmp(temp,ans);
if (flag==-1 || flag==0 && j>v) { ass(&ans,temp); v=j; }
add(&temp,t[k][j],time(d[k]*2,j));
flag=cmp(temp,ans);
if (flag!=1) { ass(&ans,temp); v=MX; }
}
double a=ans.p[0],b=ans.p[1],c=ans.p[2];
b=b/e[60]; c=c/e[60]/e[60];
a=a+b+c;
printf("%.6f ",a);
if (v>=MX) printf("Burst Evolution!\n"); else printf("%d\n",v);
}
int main(){
e[0]=1;
for (int i=1; i<=61; i++) e[i]=e[i-1]*2;
while (scanf("%d%d%d",&n,&m,&w)!=EOF){
init();
floyd();
dp();
writeit();
}
return 0;
}