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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章