您的位置:首页 > 其它

地图寻宝

2016-06-03 21:17 218 查看


contest8-T3


题目描述

BSNY来到一个小岛,小岛上有N个山洞(编号1到N),他有这个岛的地图,知道岛上的路线,也知道岛上有T个宝藏。这些宝藏分散在这N个山洞里面,当然有些洞没有宝藏,有些洞有很多宝藏, 最多有15个山洞有宝藏

BSNY通过传送先到达s个山洞,最后需要从t个山洞离开,这中间需要用最快的时间获取所有的宝藏。这些山洞有个吃掉时间的机器,每到达某个山洞,时间就会不知不觉耗掉ai时间,即使第二次到达某个山洞也同样会耗掉ai时间, 当然我们可以认为BSNY取宝藏不需要花费时间。

现在给你山洞的路线,告诉BSNY至少需要花费多少时间取走全部宝藏离开山洞。


输入

首先输入N,接下来一行输入N个整数表示ai

然后输入T,接下来一行输入T个整数,分别表示每个宝藏所在山洞的编号

然后输入P,表示山洞有P条双向边 (保证任意两个点之间可以到达

然后输入P行,每行3个数字x y z,表示山洞x和山洞y之间的距离为z

最后一行输入s, t,表示起点和终点(起点可能等于终点


输出

输出最少时间


样例输入

6
0 5 4 0 7 12
1
4
5
1 2 1
2 3 3
3 4 3
3 5 2
5 6 5
1 6


样例输出

49


提示

【样例说明】

路线为:1--->2---->3---->4--->3--->5---->6

花费为:0+1+5+3+4+3+0+3+4+2+7+5+12=49

其他样例:

6

1 2 3 4 5 6

0

5

1 2 1

2 3 3

3 4 3

3 5 2

5 6 5

1 2

输出: 4

6

1 2 3 4 5 6

0

5

1 2 1

2 3 3

3 4 3

3 5 2

5 6 5

1 1

输出:1

6

1 2 3 4 5 6

3

1 5 4

5

1 2 1

2 3 3

3 4 3

3 5 2

5 6 5

3 5

输出:39

【数据规模和约定】

1<=N<=500

0<=T<=500

0<=P<=N*(N-1)/2

0<=ai<=40000

0<=z<=40000

我们可以先最短路预处理一下,然后进行状压DP

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int n,m,p,x,y,z,s,t,all;
int a[505],b[20],c[505],e[505];
ll d[505][505];
ll f[33000][20],ans;
void dp()
{
all=(1<<m)-1;
f[0][0]=a[s];
for(int i=1;i<=all;i++)
for(int j=1;j<=m;j++) f[i][j]=1000000000LL;
for(int i=1;i<=all;i++)
for(int j=1;j<=m;j++)
{
int v=(1<<(j-1));
if((i&v)!=v) continue;
int pre=i^v;
int last=b[j];
if(pre==0) f[i][j]=min(f[i][j],f[0][0]+d[s][last]-a[s]);
else
for(int k=1;k<=m;k++)
if(k!=j)
{
int w=(1<<(k-1));
if(((i&w)==w))
if(f[pre][k]+d[b[k]][last]-a[b[k]]<f[i][j])
f[i][j]=f[pre][k]+d[b[k]][last]-a[b[k]];
}
}
ans=20000000000LL;
if(m==0) cout<<d[s][t];
else
{
for(int i=1;i<=m;i++)
ans=min(ans,f[all][i]+d[b[i]][t]-a[b[i]]);
cout<<ans;
}
}
void floyd()
{
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
cin>>m;
for(int i=1;i<=m;i++)
{
scanf("%d",&c[i]);
e[c[i]]=1;
}
m=0;
for(int i=1;i<=n;i++)
if(e[i]==1)
{
m++;
b[m]=i;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=a[i];else d[i][j]=10000000000LL;
cin>>p;
for(int i=1;i<=p;i++)
{
scanf("%d%d%d",&x,&y,&z);
d[x][y]=min(d[x][y],(ll)(a[x]+a[y])+z);
d[y][x]=min(d[y][x],(ll)(a[x]+a[y])+z);
}
cin>>s>>t;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[i][k]+d[k][j]-a[k]<d[i][j]) d[i][j]=d[i][k]+d[k][j]-a[k];
}
int main()
{
floyd();
dp();
return 0;
}


悲伤的是,500^3对于1s是过不了的,我们可以发现状压中最短路的作用只跟宝藏或起点终点有关,从而我们可以设计出这样一个方法

以起点终点和各个宝藏作为原点,每个进行一遍dijkstra,把最多17个有用点两两间的最短路保存下来就可以了。

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int n,m,p,x,y,z,s,t,all;
int a[505],b[20],c[505],e[505],flag[20];
ll d[505][505];
ll u[20][20],r[505];  //u为点对间的最短路,r用在dijkstra中
ll f[33000][20],ans;
void dp()
{
all=(1<<m)-1;
f[0][0]=a[s];
for(int i=1;i<=all;i++)
for(int j=1;j<=m;j++) f[i][j]=1000000000LL;    //f[i][j]表示在i状态下,以j号点为终点的最小值
for(int i=1;i<=all;i++)
for(int j=1;j<=m;j++)
{
int v=(1<<(j-1));
if((i&v)!=v) continue;
int pre=i^v;
if(pre==0) f[i][j]=min(f[i][j],f[0][0]+u[0][j]-a[s]);
else
for(int k=1;k<=m;k++)  //枚举pre状态的终点
if(k!=j)
{
int w=(1<<(k-1));
if(((i&w)==w))
f[i][j]=min(f[i][j],f[pre][k]+u[k][j]-a[b[k]]);
}
}
ans=20000000000LL;
if(m==0) cout<<u[0][m+1];
else
{
for(int i=1;i<=m;i++)
ans=min(ans,f[all][i]+u[i][m+1]-a[b[i]]);
cout<<ans;
}
}
void dij(int x)
{
for(int i=1;i<=n;i++)
{
r[i]=10000000000LL;
flag[i]=0;
}
r[b[x]]=a[b[x]];
for(int i=1;i<=n;i++)
{
int id=1;
ll minv=10000000000LL;
for(int j=1;j<=n;j++)
if(flag[j]==0&&r[j]<minv)
{
minv=r[j];
id=j;
}
flag[id]=1;
for(int j=1;j<=n;j++)
if(r[id]+d[id][j]-a[id]<r[j]) r[j]=r[id]+d[id][j]-a[id];
}
for(int i=0;i<=m+1;i++) u[x][i]=r[b[i]];
}
void prepare()
{
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
cin>>m;
for(int i=1;i<=m;i++)
{
scanf("%d",&c[i]);
e[c[i]]=1;
}
m=0;
for(int i=1;i<=n;i++)
if(e[i]==1)
{
m++;
b[m]=i;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=a[i];else d[i][j]=10000000000LL;
cin>>p;
for(int i=1;i<=p;i++)
{
scanf("%d%d%d",&x,&y,&z);
d[x][y]=min(d[x][y],(ll)(a[x]+a[y])+z);
d[y][x]=min(d[y][x],(ll)(a[x]+a[y])+z);
}
cin>>s>>t;
b[0]=s;
b[m+1]=t;
for(int i=0;i<=m+1;i++) dij(i);
}
int main()
{
prepare();
dp();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: