[kuangbin带你飞]专题四 最短路练习 R

166 篇文章 0 订阅
32 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=4370

HDU 4370 0 or 1(最短路)

这是整套里面我觉得最有意思的一道最短路,也确实让我觉得我与真正acmer之间的距离还是很大的

Problem Description
Given a n*n matrix Cij (1<=i,j<=n),We want to find a n*n matrix Xij (1<=i,j<=n),which is 0 or 1.

Besides,Xij meets the following conditions:

1.X12+X13+…X1n=1
2.X1n+X2n+…Xn-1n=1
3.for each i (1in), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).

For example, if n=4,we can get the following equality:

X12+X13+X14=1
X14+X24+X34=1
X12+X22+X32+X42=X21+X22+X23+X24
X13+X23+X33+X43=X31+X32+X33+X34

Now ,we want to know the minimum of ∑Cij*Xij(1<=i,j<=n) you can get.

题意:

给你一个n*n的矩阵,然后让咱们构造另一个n*n的矩阵,构造的矩阵有如下要求,
1.X12+X13+…X1n=1.
2.X1n+X2n+…Xn-1n=1.
3.for each i (1 I n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).

tip:

额。。这个题解写着好虚啊,毕竟不是自己想的。
究竟怎么就是最短路了呢?
首先啊,如果每行的数字都缩成一个点,且如果X(i,j) = 1 那么我们连边i->j,如果是0的话,乘cij之后和没有是一样的,所以不管他,那么 连边i->j什么意思呢?
第三个条件是,第i行的x和等于第i列x和对吧,那么从i出去多少条边就是第i行多少个1 ,也就是第i行的和,换句话说:i点的出度就是∑Xij 。
那么当X(i,j)=1时,第j列是不是出现了一个1?也就是说第j列的和:+1了。连边所以的i->j后,就是所有第几行第j列是1了,点j的入度自然就是第j列的和。

那么第三个条件就是: 2~n-1这些节点,满足:入度 = 出度!
第二个条件是 :n号节点的入度为1
第一个条件: 1号节点的出度为1

然后基于这个思想,每个边权值为c(i,j)
1->n的最短路就是答案,x为0的就是不选的边,下= 1的就是选的边,边的选择就交给最短路了,
这个时候,我又错了。。。
只考虑1->n是不行的,因为可能1->..->1中间没有n和n ->…->n这两个路放在一起是最小的,那么单纯的最短路就不对了咯。我们需要搞两种,在比较一下就好了,这个的处理就是之前的dist【1】先为无穷,所以1直接可达的点放队列,dijstra一下,就可以球出来1->1 1->n的最短路了,那么n->n呢?
我是选择了在一个dij,然后就ac了==

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 300*300+10;
const int maxm = 1e6+10;
int n,tot,head[maxn],dist[maxn],dis[maxn];
typedef pair<int,int>pii;
priority_queue<pii,vector<pii>,greater<pii> >q,p;
struct node{
    int v,w,next;
}edges[maxm];

void add(int u,int v,int w){
    edges[tot].v = v;edges[tot].w= w;edges[tot].next = head[u];head[u]=tot++;
}

void init(){
    memset(head,-1,sizeof(head));
    tot =0;
    dist[1] = (1<<30);
    dis[n] = (1<<30);
    for(int i =  1 ; i <= n ; i++){
        for(int j = 1 ; j <= n ; j++){
            int w;
            scanf("%d",&w);
            add(i,j,w);
            if(i == 1&&j!=1){
                q.push(make_pair(w,j));
                dist[j] = w;
            }
            if(i == n&&j!=n){
                p.push(make_pair(w,j));
                dis[j] = w;
            }
        }
    }
}

void dij(){
    while(!q.empty()){
        pii tmp = q.top();
        q.pop();
        for(int k = head[tmp.second];k!=-1;k=edges[k].next){
            if(dist[edges[k].v] > dist[tmp.second]+edges[k].w){
                dist[edges[k].v] = dist[tmp.second]+edges[k].w;
                q.push(make_pair(dist[edges[k].v],edges[k].v));
            }
        }
    }
}
void di(){
    while(!p.empty()){
        pii tmp = p.top();
        p.pop();
        for(int k = head[tmp.second];k!=-1;k=edges[k].next){
            if(dis[edges[k].v] > dis[tmp.second]+edges[k].w){
                dis[edges[k].v] = dis[tmp.second]+edges[k].w;
                p.push(make_pair(dis[edges[k].v],edges[k].v));
            }
        }
    }
}


void sov(){
    dij();
    di();
    printf("%d\n",min(dist[n],dist[1]+dis[n]));
}

int main(){
    while(~scanf("%d",&n)){
        init();
        sov();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值