2010辽宁省赛 NBUT 1221 Intermediary(三进制状压+Dijkstra)

题目链接
题意

给你 n n n 个点 0 0 0 n − 1 n-1 n1 m m m 个行政人员 0 0 0 m − 1 m-1 m1 q q q 条边。
接下来一行 m m m 个整数,代表 e i e_i ei
接下来一行 m m m 个整数,代表 f i f_i fi
接下来 q q q 行,每行 4 4 4 个整数, u u u v v v z z z d d d,表示 u u u v v v 有条单向边,归第 z z z 个行政人员管,边权为 d d d
0 0 0 号点到 n − 1 n-1 n1 号点建立联系的最小花费,即最短路。
两点间最短路除了花费 d d d 以外,还需要加上 e e e f f f
m i m_i mi 个行政人员第二次工作需要花费 d + e i d+e_i d+ei,超过三次工作需要花费 d + f i d+f_i d+fi

思路

先三进制状态压缩,工作0次,1次,>1次。
求最短路时,多考虑一个所有行政人员工作状态(次数)
d i s [ 行 政 人 员 状 态 ] [ 当 前 点 ] dis[行政人员状态][当前点] dis[][],跑个dijkstra即可。

后台数据好像略水,应该没有超过 n > 10 n>10 n>10 的?,数组开小了竟然A了。

#include <stdio.h>
#include <vector>
#include <queue>
#include <string.h>
using namespace std;

int read(int &_a){return scanf("%d",&_a);}
int rd(){int _tmp; scanf("%d",&_tmp); return _tmp;}

struct Node
{
    int v, id, d;
}node;
vector<Node> e[105]; // 存图
int w[10], f[10]; // 存权值
// vis[行政人员状态][当前点] 表示来过没,感觉瞎搞下可以用dis代替
// dis[行政人员状态][当前点] 表示最短路
int three[15], dig[60000][15], vis[60000][105], dis[60000][105];

struct Point
{
    int u, dis, wtf;
    Point(){}
    Point(int _a, int _b, int _c):u(_a),dis(_b),wtf(_c){}
    bool operator <(const Point &a) const
    {
        return dis > a.dis;
    }
}st, ne;

int get(int wtf, int id)
{
    if(dig[wtf][id] == 0) return 0;
    if(dig[wtf][id] == 1) return w[id];
    return f[id];
}

int main()
{
    /*三进制压缩预处理*/
    three[0] = 1;
    for(int i = 1; i <= 10; ++i) three[i] = 3*three[i-1];
    for(int i = 0; i < three[10]; ++i)
    {
        int tmp = i;
        for(int j = 0; j < 10; ++j)
        {
            dig[i][j] = tmp%3;
            tmp /= 3;
        }
    }
    int n, m, q;
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        /*输入*/
        for(int i = 0; i < n; ++i) e[i].clear();
        for(int i = 0; i < m; ++i) read(w[i]);
        for(int i = 0; i < m; ++i) read(f[i]);
        for(int i = 0; i < q; ++i)
        {
            int u = rd();
            node.v = rd(), node.id = rd(), node.d = rd();
            e[u].push_back(node);
        }
        
        /*求最短路*/
        priority_queue<Point> q;
        q.push(Point(0,0,0));
        int flag = 1;
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        dis[0][0] = 0;
        while(!q.empty())
        {
            st = q.top();
            q.pop();
            if(st.u == n-1)
            {
                flag = 0;
                printf("%d\n",st.dis);
                break;
            }
            if(vis[st.wtf][st.u]) continue;
            vis[st.wtf][st.u] = 1;
            for(int i = e[st.u].size()-1; ~i; --i) // 多考虑了一个状态,但是和普通最短路算法没啥区别
            {
                Node v = e[st.u][i];
                ne.dis = st.dis+v.d+get(st.wtf,v.id);
                ne.wtf = st.wtf;
                if(dig[st.wtf][v.id] < 2) ne.wtf = st.wtf+three[v.id];
                ne.u = v.v;
                if(dis[ne.wtf][ne.u] <= ne.dis) continue;
                dis[ne.wtf][ne.u] = ne.dis;
                q.push(ne);
            }
        }
        if(flag) printf("-1\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值