二分+最短路--P8794 [蓝桥杯 2022 国 A] 环境治理

12 篇文章 0 订阅

好好读题比啥都重要

多源汇最短路,再加上 n 的范围如此之小,很明显是用floyd来做

比较暴力的写法是每一天,清洁完一轮,减少一轮灰尘度以后,执行一次floyd求最短路,然后再算P

#include <bits/stdc++.h>
using namespace std;

bool flag;
bool judge[105][105];
int sum;
int n,Q;
int D[105][105],L[105][105];
int f[105][105];

int edge(int d)    //求总共有多少条边
{
    int temp = 0;
    for(int i = d - 1; i >= 1; i --) temp += i;
    
    return temp;
}

void floyd()
{
    for(int i = 0; i < n; i ++)   //先初始化
    {
        for(int j = 0; j < n; j ++)
        {
            f[i][j] = D[i][j];
        }
    }
    for(int i = 0; i < n; i ++)  //floyd标准三层循环求最短路
    {
        for(int j = 0; j < n; j ++)
        {
            for(int k = 0; k < n; k ++)
            {
                f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
            }
        }
    }
    
    return;
}

int p()      //用来求P
{
    int P = 0;
    
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        P += f[i][j];
    }
    return P;
}

void clear(int x)      //清理道路 x表示城市序号
{
    x = x % n;
    for(int i = 0; i < n; i ++)
    {
        if(i == x) continue;         //自己跟自己就不用管了
        if(judge[i][x] || judge[x][i]) continue;   //这条路已到达下限
        else
        {
            if(D[i][x] == L[i][x])
            {
                judge[i][x] = 1;
                judge[x][i] = 1;
                sum++;              //表示有多少条边已经到达下限了
            }
            else
            {
                D[i][x] -= 1;       //清理
                D[x][i] -= 1;
            }
        }
    }
    
    return;
}

int main()
{
    cin >> n >> Q;
    
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        {
            cin >> D[i][j];
        }
    }
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        {
            cin >> L[i][j];
        }
    }
    
    for(int i = 0; !flag; i ++)
    {
        floyd();
        if(p() <= Q) {cout << i; break;}
        clear(i);
        if(sum == edge(n)) flag = 1;
    }
    
    if(flag) cout << -1;
    return 0;
}

floyd时间复杂度是n的三次方,n是100的话就10的六次方左右,感觉也差不多吧,于是交了一发暴力70分。。蓝桥国赛的数据还是不能轻视啊。。。

想来想去,偷偷看标签才知道 

因为随着天数的增加,灰尘度一定单调递减,有这样的单调性,得用二分来优化啊

对天数进行二分,用check来判断是否符合要求,check里面对于每一个城市的治理次数要仔细判断,具体看代码;mod n 别忘了

#include <bits/stdc++.h>
using namespace std;

bool flag;
int n,Q;
int D[105][105],L[105][105];
int f[105][105];

void floyd()
{
    for(int i = 0; i < n; i ++)  //floyd标准三层循环求最短路
    {
        for(int j = 0; j < n; j ++)
        {
            for(int k = 0; k < n; k ++)
            {
                f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
            }
        }
    }
    
    return;
}

int p()      //用来求P
{
    int P = 0;
    
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        P += f[i][j];
    }
    return P;
}

bool check(int x)
{
    int clear = x/n;       //表示每个城市至少经历了多少轮治理
    int city = (x - 1)%n;   //表示有哪些城市是正在进行最新一轮的治理
    for(int i = 0; i < n; i ++)   //先初始化
    {
        for(int j = 0; j < n; j ++)
        {
            f[i][j] = D[i][j];
        }
    }
    
    if(!x)      //第0天就特判一下,直接floyd
    {
        floyd();
        if(p() <= Q) return true;
        else return false;
    }
    
    for(int i = 0; i < n; i ++)
    {
        for(int j = i + 1; j < n; j ++)
        {
            int dif;
            if(i <= city) dif = clear + 1;  //序号在city之后的城市是还没有轮到最新一轮治理的,治理数会小1
            else dif = clear;
            
            //接下来进行环境治理
            if(D[i][j] - dif >= L[i][j])
            {
                f[i][j] = D[i][j] - dif;
                f[j][i] = D[i][j] - dif;
            }
            else
            {
                f[i][j] = L[i][j];
                f[j][i] = L[i][j];
            }
        }
    }
    floyd();
    if(p() <= Q) return true;
    else return false;
}

int main()
{
    cin >> n >> Q;
    
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        {
            cin >> D[i][j];
        }
    }
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j ++)
        {
            cin >> L[i][j];
        }
    }
    
    int l = 0, r = 100000;        //二分咯
    while(l < r)
    {
        int mid = (l + r)/2;
        if(check(mid)) flag = 1, r = mid;   //如果有成功的flag变true
        else l = mid + 1;
    }
    
    if(!flag) cout << -1;
    else cout << l;      //输出的是左指针l哦
    
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Silver_Bullet14

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值