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

来源:互联网 发布:访客营销效果数据每封 编辑:程序博客网 时间:2024/06/01 20:50

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 10005struct 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 0
原创粉丝点击