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;
}

 

 

posted on 2012-10-29 16:34  AlphaX  阅读(241)  评论(0编辑  收藏  举报

导航