NBUT 1221 Intermediary(优先队列+dijkstra)(状态压缩)

115 篇文章 0 订阅
68 篇文章 0 订阅

Intermediary

问题描述
It is widely known that any two strangers can get to know each other through at most six other people. Now let’s prove this.

In the country Intermediary Conducts Personal Communications (ICPC), there are up to n (2<=n<=100) ordinary people conveniently numbered from 0 to n-1. They don’t know each other, or, in other words, they are strangers. The only way they can communicate with each other is through the government, which, in fact, is an intermediary agency. The government consists of up to m (1<=m<=9) employees conveniently numbered from 0 to m-1. Suppose employee z can introduce person x to person y at a cost of d dollars. If this is the first time in a day that employee z introduce one person to another, he will only require d dollars. For the second time, he will require d dollars plus extra e dollars as his tip. For the third time and more, he will require d dollars plus extra f dollars. He is not dared to require any more than that since the strange country is somewhat democratic. And if person x is able to communicate with person t and person t is able to communicate with person y, then person t is always willing to transfer messages from person x to person y, at no charge. Of course, the intermediary fees are all paid by person x. Notice that employee z being able to introduce person x to person y doesn’t mean he can introduce person y to person x.

Now person 0 has to send a message to person n-1 in one day. If all employees have just started to work, what is the minimum cost for person 0?

输入
For each test case, the first line contains three integers, n, m and q, where q is the number of intermediary relationships and q is at most 10,000. The second line has m integers, each indicating the value e of every employee, in the range [0, 100]. The third line has m integers too, each indicating the value f of every employee, in the range [e, 200]. The next q lines each contains four integers, x, y, z and d, indicating that employee z can introduce person x to person y requiring d dollars, where 1<=d<=200. There is a blank line after each test case.
Proceed to the end of file.
输出
For each test case, print one integer on a single line, giving the minimum cost. If it is impossible, print -1.
样例输入
3 2 2
1 1
2 2
0 1 0 1
1 2 1 2

5 1 4
1
2
0 1 0 1
1 2 0 1
2 3 0 1
3 4 0 1
样例输出
3
9
来源
辽宁省赛2010

思路:最短路的思想很容易想到,最关键的就是如何动态地更新这条路上每一个employee的使用次数

看了大牛的博客之后想了好久才勉强理解大牛的三进制状态压缩法

dis[i][j]表示到i节点,每个中介用了几次的情况下的最小花费

先上代码吧,看一下状态压缩法的步骤

代码:

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

#define mem(a,b) memset(a,b,sizeof(a))
const int inf=0x3f3f3f3f;
#define maxn 105
#define maxv 10005

struct node
{
    int v,z,d,next;
} G[maxv];
struct Node
{
    int u,cnt,dis;
    Node(int U,int CNT,int DIS)//构造函数
    {
        u=U,cnt=CNT,dis=DIS;
    }
    bool operator < (const Node&x)const//重载操作符<,最小值优先
    {
        return dis>x.dis;
    }
};
int first[maxn],co1[maxn],co2[maxn],employ[10],d[maxn][20000],re[10];
int n,m,cc,len;

void dijkstra()
{
    for(int i=0;i<n;++i)
        for(int j=0;j<employ[m];++j)
            d[i][j]=inf;
    d[0][0]=0;
    priority_queue<Node>Q;
    Q.push(Node(0,0,0));
    while(!Q.empty())
    {
        Node q=Q.top();
        Q.pop();
        if(q.dis>d[q.u][q.cnt])//已经是更优的解了,很明显不需要松弛
            continue;
        if(q.u==n-1)//松弛到达n-1
        {
            printf("%d\n",q.dis);
            return ;
        }
        for(int i=first[q.u]; i!=-1; i=G[i].next)
        {
            int tmp=q.cnt;
            for(int j=0;j<m;++j)//更新每一个employee在这条路上的使用次数
                re[j]=tmp%3,tmp/=3;
            int v=G[i].v,z=G[i].z,cost=G[i].d,t=1;
            if(re[z]==1)
                cost+=co1[z];
            else if(re[z]==2)
                cost+=co2[z],t=0;
            if(d[v][q.cnt+t*employ[z]]>q.dis+cost)
            {
                d[v][q.cnt+t*employ[z]]=q.dis+cost;
                Q.push(Node(v,q.cnt+t*employ[z],d[v][q.cnt+t*employ[z]]));
            }
        }
    }
    printf("-1\n");
}

void add_egde(int u,int v,int z,int d)
{
    G[len].v=v,G[len].z=z,G[len].d=d;
    G[len].next=first[u];
    first[u]=len++;
}

int main()
{
    employ[0]=1;
    for(int i=1;i<=9;++i)//进行初始化
        employ[i]=3*employ[i-1];
    while(~scanf("%d%d%d",&n,&m,&cc))
    {
        mem(first,-1);
        for(int i=0; i<m; ++i)
            scanf("%d",&co1[i]);
        for(int i=0; i<m; ++i)
            scanf("%d",&co2[i]);
        len=0;
        int x,y,z,d;
        for(int i=0; i<cc; ++i)
        {
            scanf("%d%d%d%d",&x,&y,&z,&d);
            add_egde(x,y,z,d);
        }
        dijkstra();
    }
    return 0;
}



也就是说要先对employee进行初始化处理,然后每次松弛前先更新在这条路上的employee的使用次数,之后再进行松弛比较

对于d[i][j]还是代入数据比较好理解,比如说有这样一组数据:
5 3 4
1 1 1
2 2 2
0 1 0 1
1 2 0 1
2 3 2 1
3 4 1 2
很明显,初始的时候d[0][0]=0,
在松弛第一条边之前,先更新employee使用次数,所有的re[]都为0,之后松弛第一条边,变为d[1][1]=1,代表0到1这条路,雇员0用了一次

接下来松弛第二条边之前更新employee的使用次数,只有re[0]=1,之后松弛第二条边,变为d[2][2]=3,代表0到2这条路,雇员0用了两次

更新employee使用次数,re[0]=2,之后松弛第三条边,变为d[3][11]=4,代表0到3这条路,雇员0用了两次,雇员2用了一次

更新employee使用次数,re[0]=2,re[2]=1,之后松弛第四条边,变为d[4][14]=6,代表0到4这条路,雇员0用了两次,雇员2用了一次,雇员1用了一次
更新employee使用次数,re[0]=2,re[1]=1,re[2]=1

更新employee使用次数说白了就是,在d[i][j]的j中寻找有多少个1(就是雇员0),多少个3(雇员1),多少个9(雇员2),多少个27(雇员3)………..

大致思路就是这样了,菜鸟比赛时写了个全局的记录使用次数的方法(局限是在不该更新次数的时候更新了)。。。
菜鸟又get到了新方法—-三(多)进制状态压缩法

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值