CodeForces 716B Complete The Graph(二分搜索)(最短路)

题目链接:http://codeforces.com/problemset/problem/715/B

B. Complete The Graph
time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
ZS the Coder has drawn an undirected graph of n vertices numbered from 0 to n - 1 and m edges between them. Each edge of the graph is weighted, each weight is a positive integer.

The next day, ZS the Coder realized that some of the weights were erased! So he wants to reassign positive integer weight to each of the edges which weights were erased, so that the length of the shortest path between vertices s and t in the resulting graph is exactly L. Can you help him?

Input
The first line contains five integers n, m, L, s, t (2 ≤ n ≤ 1000,  1 ≤ m ≤ 10 000,  1 ≤ L ≤ 109,  0 ≤ s, t ≤ n - 1,  s ≠ t) — the number of vertices, number of edges, the desired length of shortest path, starting vertex and ending vertex respectively.

Then, m lines describing the edges of the graph follow. i-th of them contains three integers, ui, vi, wi (0 ≤ ui, vi ≤ n - 1,  ui ≠ vi,  0 ≤ wi ≤ 10^9). ui and vi denote the endpoints of the edge and wi denotes its weight. If wi is equal to 0 then the weight of the corresponding edge was erased.

It is guaranteed that there is at most one edge between any pair of vertices.

Output
Print “NO” (without quotes) in the only line if it’s not possible to assign the weights in a required way.

Otherwise, print “YES” in the first line. Next m lines should contain the edges of the resulting graph, with weights assigned to edges which weights were erased. i-th of them should contain three integers ui, vi and wi, denoting an edge between vertices ui and vi of weight wi. The edges of the new graph must coincide with the ones in the graph from the input. The weights that were not erased must remain unchanged whereas the new weights can be any positive integer not exceeding 10^18.

The order of the edges in the output doesn’t matter. The length of the shortest path between s and t must be equal to L.

If there are multiple solutions, print any of them.

题目大意:给定一个n个点,m条边的图,其中有部份边的权重未知(所有边的权重都为正整数)。求一种可能的权重分配,使得从给定的点s到t的距离为l;如果存在这样的分配,则打出所有的边与其权重,如果不存在,直接输出”NO”。

题目解析:
我们把没有被分配到权重的边,叫做未知边
首先思考什么时候会存在解。当把所有未知边看做权重为1时,如果s到t的距离大于l,那么肯定不存在解。因为1是边所能得到的最小的权重值,而增加未知边的权重一定不会使得s到t的距离减小,所以不存在解使得s到t的距离为l;当把所有未知边的权重看作为无穷大的时候(10^9),如果s到t的距离小于l,那么肯定也不存在解。因为减小未知边的权重一定不会使s到t的距离增加,所以不存在解。

其次,由于只需找出一组可能的分配方案。我们将一部分的未知边(数量为x)的权重设为1,剩下的设为无穷大。二分搜索找出最小的x使得此时的s到t的距离小于等于l,这时候由于找到的x是最小的满足s到t的距离小于l的,所以如果将x点的权重由1改为无穷大,就会使最短距离大于l,这时候用二分搜索找到x点的权重。

知识点:二分搜索,Dijk最短路算法


AC代码如下:

#include <iostream>
#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include <stdio.h>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <string>
#include <string.h>
#include <sstream>
#include <cctype>
#include <climits>
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <vector>
#include <iterator>
#include <algorithm>
#include <stack>
#include <functional>
//cout << "OK" << endl;
#define _clr(x,y) memset(x,y,sizeof(x))
#define _inf(x) memset(x,0x3f,sizeof(x))
#define pb push_back
#define mp make_pair
#define FORD(i,a,b) for (int i=(a); i<=(b); i++)
#define FORP(i,a,b) for (int i=(a); i>=(b); i--)
#define REP(i,n) for (int i=0; i<(n); i++)
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const double EULER = 0.577215664901532860;
const double PI = 3.1415926535897932384626;
const double E = 2.71828182845904523536028;
typedef long long LL;
LL pow_mod(LL a,LL n,LL m)
{
    if(n == 0) return 1;
    LL x = pow_mod(a,n>>1,m);
    LL ans = x*x%m;
    if(n&1) ans = ans*a%m;
    return ans;
}
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}

const int N = 1001;
struct edge{
    int to;
    LL weight;
    int label;//给定权重的边的为0,未给定权重的边的为编号
    edge(int _to, LL _weight,int _label){
        to = _to;
        weight = _weight;
        label = _label;
    }
};
vector<edge> adj[N];
LL dist[N];
set<pair<int,int> > used;//输出时用做判断是否已经边是否已经输出过
int n;//点数
int m;//边数 
int s;//出发点
int e;//到达点

LL dijk(int num, int value){//num为将num-1条为给定权重的边的权重定位1,value位将编号为num的为给定权重的边的权重设为value
    for(int i = 0; i < n; i++){
        dist[i] = INF;
    }
    dist[s] = 0;
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > pq;
    pq.push(mp(0,s));
    while(!pq.empty()){
        int d = pq.top().first;
        int u = pq.top().second;
        pq.pop();
        for(int i = 0; i < adj[u].size(); i++){
            edge& tmp = adj[u][i];
            int v = tmp.to;
            LL w = tmp.weight;
            int label = tmp.label;
            //处理未知边的权重
            if(label > 0){
                if(label < num) w = 1;
                else if(label == num) w = value;
                else w = INF;
            }
            if(d + w < dist[v]){
                dist[v] = d + w;
                pq.push(mp(dist[v],v));
            }
        }
    }
    return dist[e];
}

int l,edgecnt;//edgecnt为未知边的编号

void read(){
    edgecnt = 0;
    FORD(i,0,m-1){
        int lable = 0;
        int u,v,w;
        cin >> u >> v >> w;
        if(!w){
            edgecnt++;
            lable = edgecnt;
        }
        adj[u].pb(edge(v,w,lable));
        adj[v].pb(edge(u,w,lable)); 
    }
}
//得到结果后构造新的图
void makegraph(int num, LL val){
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < adj[i].size(); j++)
        {
            int lab = adj[i][j].label;
            if(lab > 0)
            {
                if(lab < num)
                {
                    adj[i][j].weight = 1;
                }
                else if(lab == num)
                {
                    adj[i][j].weight = val;
                }
                else
                {
                    adj[i][j].weight = INF;
                }
            }
        }
    }
}
//输出图
void output(){
    cout << "YES" << endl;
    FORD(i,0,n-1){
        for(int j = 0; j < adj[i].size(); j++){
            edge e = adj[i][j];
            if(used.find(mp(i,e.to)) == used.end() && used.find(mp(e.to,i)) == used.end()){
                cout << i << " " << e.to << " " << e.weight << endl;
                used.insert(mp(i,e.to));
                used.insert(mp(e.to,i));
            }
        }
    }
}

int main(){
    cin >> n >> m >> l >> s >> e;
    read();
    //判断是否存在解,未知边都为无穷大与1时的情况
    LL tmp1 = dijk(edgecnt, 1);
    LL tmp2 = dijk(0, 1);
    if(tmp1 <= l && tmp2 >= l){
        LL lo = 0, hi = edgecnt, mid, ans;
        //二分查找得到最小的数量的未知边的权重为1使得,s到t的距离小于等于l
        while(lo <= hi){
            mid = (lo+hi)>>1;
            if(dijk(mid,1) <= l){
                ans = mid;
                hi = mid - 1;
            }
            else lo = mid + 1;
        }
        //如果是0需要特判
        if(ans == 0){
            makegraph(-1,0);
            output();
            return 0;
        }
        //如果不为0,则二分搜索第ans条未知边的权重值
        LL result;
        lo = 0; hi = INF;
        while(lo <= hi){
            mid = (lo+hi)>>1;
            if(dijk(ans,mid) <= l){
                result = mid;
                lo = mid + 1;
            }
            else hi = mid - 1;
        }
        makegraph(ans,result);
        output();
    }
    else cout << "NO" << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章