tyvj-1288-飘飘乎居士取能量块
传送门:http://new.tyvj.cn/Problem_Show.aspx?id=1288
一道小技巧的图论
先Floyed预处理出每两点的最短路径
然后DP。方程: f[i][k]
表示 从1到达i经过k集合中的点后到达n最小费用
这里的k集合指的是一串二进制。
例如:1001表示经过第一个点和第四个点。
当然这里要把离散的各个放着能量的点重新标号。
因为最多有十个能量点,所以是可行的
转移方程:
f[i][k]=min( f[j][get(k-j)] + g[j][i])
这里get()的意思是原先k集合拿掉j这个点后新的集合,j就是拿掉的点在图中的标号
答案就是 f[n][(1<<p)-1]
【Code】
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <math.h> using namespace std; #define INTMAX 0x7ffffff1 int n;//图大小 int g[110][110]; //路径权值 (最短路) int f[110][2048]; // DP int d[110]; // 有能量块的顶点离散化 int p; void init() { cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>g[i][j]; cin>>p; for(int i=1;i<=p;i++) cin>>d[i]; //floyed for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if( i!=j && g[i][k] < g[i][j]-g[k][j] ) g[i][j]=g[i][k]+g[k][j]; } int lowbit(int x) { return x-(x&(x-1)); } int Work(int node,int state) { if( f[node][state] ) return f[node][state]; int tmp=state,t; int ans=INTMAX; for( int i=lowbit(tmp);tmp!=0;i=lowbit(tmp) ) { tmp-=i; //遍历全部位置 t=state-i; //上一次的状态 i=d[ int(log2(i))+1 ]; //拿到上一次的点 ans=min( ans,Work(i,t)+g[i][node] ); } f[node][state]=ans; return ans; } int main() { init(); for(int i=1;i<=n;i++) //初始值 f[i][0]=g[1][i]; cout<<Work(n,(1<<p)-1)<<endl; return 0; }