ZOJ 1232 Adventure of Super Mario(图DP)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

题意:给出a个村庄,b个城堡,村庄的标号为1……a,城堡的标号为a+1……a+b。然后有m条无向边,每条边有个距离。某个人打算从a+b到1,然后他有一双神奇的鞋子,可以用k次,每次最多连续跑L。而且穿着鞋子的时候不能穿过城堡,到达城堡的时候只能停下,而且不能停留在路中间。只要停下来,就相当于用了一次鞋子。 

今天做完TC,对于div1的450,是一个新接触的。在spfa中跑DP。有效的解决了图上DP的后效性问题。
yobobobo之后推荐了这个题,主要还是受TC的影响,才能想到正解。
首先预处理下穿鞋子跑一次的情况。
枚举所有点为起点,然后预处理从这个点穿着鞋子跑到达每一个点的最短距离。
而且注意:不能穿过城堡
在SPFA的时候,不会把城堡入队(除了城堡是起点)
之后dp[i][j]表示在i点时,鞋子还剩j次的最短距离。
DP过程扔进spfa里更新就行了。
对于每一个点,更新情况有两种。
首先不考虑鞋子,步行到下一个地方。
考虑用一次鞋子,枚举所有的点,进行转移。
#include<iostream>    
#include<cstdio>    
#include<map>    
#include<cstring>    
#include<cmath>    
#include<vector>    
#include<algorithm>    
#include<set>    
#include<stack>  
#include<string>    
#include<ctime>  
#include<queue>    
#define inf 0x3f3f3f3f   
#define M 10000005   
#define N 40005  
#define maxn 210005    
#define eps 1e-8  
#define zero(a) fabs(a)<eps    
#define Min(a,b) ((a)<(b)?(a):(b))    
#define Max(a,b) ((a)>(b)?(a):(b))    
#define pb(a) push_back(a)    
#define mp(a,b) make_pair(a,b)    
#define mem(a,b) memset(a,b,sizeof(a))    
#define LL long long    
#define MOD 1000000007  
#define sqr(a) ((a)*(a))    
#define Key_value ch[ch[root][1]][0]    
#define test puts("OK");    
#define pi acos(-1.0)  
#define lowbit(x) ((-(x))&(x))  
#define HASH1 1331  
#define HASH2 10001  
#define C   240    
#define vi vector<int>  
#define TIME 10    
//#pragma comment(linker, "/STACK:1024000000,1024000000")    
using namespace std;
struct Edge{
    int v,w,next;
}e[10005];
int t,a,b,m,l,k;
int start[105],tot;
int dist[105][105];
int dp[105][15];
void _add(int u,int v,int w){
    e[tot].v=v;e[tot].w=w;
    e[tot].next=start[u];
    start[u]=tot++;
}
void add(int u,int v,int w){
    _add(u,v,w);
    _add(v,u,w);
}
void Init(){
    queue<int>que;
    bool in[105];
    mem(dist,0x3f);
    for(int i=1;i<=a+b;i++){
        while(!que.empty()) que.pop();
        mem(in,false);
        que.push(i);
        in[i]=true;
        dist[i][i]=0;
        while(!que.empty()){
            int u=que.front();
            que.pop();
            in[u]=false;
            for(int r=start[u];r!=-1;r=e[r].next){
                int v=e[r].v,w=e[r].w;
                if(dist[i][v]>dist[i][u]+w){
                    dist[i][v]=dist[i][u]+w;
                    if(v<=a&&in[v]==false){
                        in[v]=true;
                        que.push(v);
                    }
                }
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    while(t--){
        mem(start,-1);
        tot=0;
        scanf("%d%d%d%d%d",&a,&b,&m,&l,&k);
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        Init();
        bool in[105];
        mem(in,false);
        mem(dp,0x3f);
        queue<int>que;
        que.push(a+b);
        in[a+b]=true;
        dp[a+b][k]=0;
        while(!que.empty()){
            int u=que.front();
            que.pop();
            in[u]=false;
            for(int i=0;i<=k;i++){
                for(int r=start[u];r!=-1;r=e[r].next){
                    int v=e[r].v,w=e[r].w;
                    if(dp[v][i]>dp[u][i]+w){
                        dp[v][i]=dp[u][i]+w;
                        if(in[v]==false){
                            in[v]=true;
                            que.push(v);
                        }
                    }
                }
                if(!i) continue;
                for(int v=1;v<=a+b;v++){
                    if(v!=u&&dist[u][v]<=l){
                        if(dp[v][i-1]>dp[u][i]){
                            dp[v][i-1]=dp[u][i];
                            if(in[v]==false){
                                in[v]=true;
                                que.push(v);
                            }
                        }
                    }
                }
            }
        }
        int ans=inf;
        for(int i=0;i<=k;i++)
            ans=min(ans,dp[1][i]);
        printf("%d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值