地图寻宝
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; }
相关文章推荐
- Android的sdk23及以上版本中的权限处理
- 死锁的产生以及如何防止死锁的产生
- 矩阵顺时针旋转90度
- 第二阶段冲刺第十天
- poj 3204(最小割--关键割边)
- Windows7+VS2012下OpenGL 4的环境配置
- hdu2044 一只小蜜蜂....
- [Ruby笔记]22.Ruby :: namespace 以及 instance method 与class method
- 华为项目管理10大模板Excel版(可直接套用_非常实用)
- js-shortid:优雅简洁地实现短ID
- 阿里云Hadoop开发自动打包上传运行maven的pom.xml
- c++作业7
- 第116课: Spark Streaming性能优化:如何在毫秒内处理处理大吞吐量的和数据波动比较大 的程序
- Android Studio常用快捷键、Android Studio快捷键大全
- 浪潮之巅 科技之巅
- 个人最终总结
- storm集群搭建
- https相关原理
- 查找两个数组的公共元素
- 观察者模式