Codeforces Round #601 (Div. 2)B. Fridge Lockers

B. Fridge Lockers

【題目類型】模擬

【題目鏈接】B題鏈接

【題目大意】給你n個冰箱,m個鐵鏈對這n個冰箱連接,要求使用所有的鐵鏈對冰箱進行連接,滿足非冰箱主人不能獨自打開,u冰箱連接v冰箱的花費是av+au求解最小花費是多少?以及最小花費時的連接情況

A fridge is private if and only if, among n people living in the apartment, only the owner can open it (i.e. no other person acting alone can do it).
冰箱是私人的,在所有公寓裏住的人當中,只有主人可以打開(其他人不可以獨自打開它)
就是在主人不在的情況下,其他人不能獨自打開它,只能其他人一起行動打開它

【解題思路】有這麼四個個注意點
(1)冰箱是一定得連接的,而且必須連接兩個冰箱,這樣才能保證不會被一個非主人獨自打開
(2)如果鐵鏈的很多的話,兩個冰箱之間是可以重複連接的,這樣多出來的鐵鏈就可以重複連接花費最小的兩個冰箱從而達到花費少
(3)是不存在自己連接自己的情況的
(4)輸入是沒有按照升序或者降序的方式輸入的,不要多想
(5)最關鍵的一點是 n == 2的時候無論鐵鏈有多少條都是不可能連接成功的,當時貌似很多人都卡在這一點

On the other hand, if there are n=2 fridges and only one chain (which connects them) then both fridges are not private (both fridges can be open not only by its owner but also by another person).
所以兩個冰箱一條鐵鏈也是成立的

這裏有這麼一組數據可以對解題思路有所思考

  1. input
  2. 3 3
  3. 1 2 3
  4. output
  5. 12
  6. 1 2
  7. 1 3
  8. 2 3

黑色圈表示的是冰箱,同時圈中數字表示連接上一冰箱所需的耗費,橙色表示鐵鏈,紅色數字表示該鐵鏈所需要的花費
黑色圈表示的是冰箱,同時圈中數字表示連接上一冰箱所需的耗費,橙色表示鐵鏈,紅色數字表示該鐵鏈所需要的花費
可以發現

  1. 這三個冰箱是成環的,於是很清楚的滿足了,一個冰箱連接非自己的兩個冰箱冰箱的條件
  2. 每個冰箱連接的都是除了自己外兩個花費最小的冰箱

**那接下來就有思路了,如果按2每次都去查找非自己最小的兩個花費的話,n個數字,每次要進行n-1次查詢出自己外第一小的數字和第二小的數字, **

大概是每個數字是這麼一個查詢次數在這裏插入圖片描述
那好很明顯,超時的可能性很大,因爲n個數字,時間複雜度就要達到n^3了,於是思路就要從成環上入手
每個冰箱最少要連接兩個冰箱,那麼就要提供最少兩次的 ai 無論你是怎麼連,最少兩次,那麼直接和左右的 al 和 ar冰箱進行連接就可以了,那麼就提供了兩次權值,

如果沒有鐵鏈剩餘且每個冰箱連接了兩個冰箱那不就成立了嗎?

好!那接下來考慮還有鐵鏈沒有連接的情況注意點中有提及,題目是允許重複連接兩個冰箱的,那麼剩下的鐵鏈全部去連接花費第一小的冰箱和花費第二小的冰箱就可以了。

/**
 *    This code has been written by YueGuang, feel free to ask me question. Blog: http://www.yx.telstudy.xyz
 *    created:
 */
#include <cstdio>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REP_(i, a, b) for(int i = a; i <= b; i++)
#define sl(n) scanf("%lld", &n);
#define si(n) scanf("%d", &n);
#define RepAll(a) for(auto x: a)
#define cout(ans) cout << ans << endl;
typedef long long ll;

using namespace std;
const int maxn = 1e5+50;
int a[maxn];
int main(){
    //freopen("in.txt", "r", stdin);

    int t; scanf("%d", &t);
    while(t--){
        int n, m; scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
        if (m < n||n == 2){printf("-1\n"); continue;}

        int ans = 0;

        //查找最小的和第二小的
        int mi1 = 1, mi2 = 1;
        for(int i = 1; i <= n; i++){
            if (a[i] < a[mi1]){mi1 = i;}
        }
        if (mi1 == 1){mi2 = 2;}
        else mi2 = 1;

        for(int i = 1; i <= n; i++){
            if (a[i] < a[mi2] && mi1 != mi2){
                mi2 = i;
            }
        }

        for(int i = 1; i+1 <= n; i++){
            ans += a[i]+a[i+1];
        }
        ans += a[1]+a[n];
        for(int i = n+1; i <= m; i++){
            ans += a[mi1]+a[mi2];
        }
        cout << ans << '\n';
        for(int i = 1; i+1 <= n; i++){
            cout << i << " " << i+1 << '\n';
        }
        cout << 1 << " " << n << '\n';
        for(int i = 1; i <= m-n; i++){
            cout << mi1 << " " << mi2 << '\n';
        }

    }
}

代碼未在時間複雜度或者空間複雜度上做出優化,歡迎在評論區或者我的郵箱[email protected]中提出,也可以對我的代碼風格提出意見,非常感謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章