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

Codeforces Round #601 (Div. 2)B. Fridge Lockers_第1张图片
黑色圈表示的是冰箱,同时圈中数字表示连接上一冰箱所需的耗费,橙色表示铁链,红色数字表示该铁链所需要的花费
可以发现

  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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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]中提出,也可以对我的代码风格提出意见,非常感谢

你可能感兴趣的:(Codeforces Round #601 (Div. 2)B. Fridge Lockers)