蓝桥杯 包子凑数 最短路

看了网上很多完全背包的题,都感觉没解释清楚。

设包子数为 a1 a2 a3 a4 a5...an

考虑 对于凑一个 x 数

k1 * a1 + k2*a2 +... +kn*an = x

两边mod a1

(k2*a2%a1 +k3*a3%a1 + ... +kn*an%a1 )%a1= x%a1

x%a1 的范围为 [0,a1);

比如说  想凑 x = 31,a1 = 5; x%a1 = 1;

我能通过 (k2*a2%a1 +k3*a3%a1 + ... +kn*an%a1 )%a1 组合能凑出一个1

那么 我只要 将能凑出 1的 (k2*a2%a1 +k3*a3%a1 + ... +kn*an%a1 )%a1组合 再加上5个a[1] , 即可凑出来

但是 有个情况  就是 (k2*a2%a1 +k3*a3%a1 + ... +kn*an%a1 )%a1凑出来的是mod之后的数 可能原这个数为 31 也可能是41 51 . 如果是51的话 那么31肯定是无法组合的

所以我们用最短路求解 

d[i] 表示 凑出 x%a[1] =i 所需要的数最小合

建边 就是 

mp[j][(j+a[i])%a[0]]=min(mp[j][(j+a[i])%a[0]],a[i]);

比如说 对于 a[1] = 5 a[2]=8 a[2]%a[1]=3

那么 对于 结果余数为1的情况 , 可以通过加一个 a[2] 变成余数为4 所以 1->4有路径,路径费用为a[2] = 8;

这样 一次最短路后 ,

对 i ~ [0,a[1])范围

如果存在 d[i]=inf 那么说明这个点不可达 所以 任意 k*a[1]+i (该数mod a[1] = i) 都不能组合 所以此时结果为inf

如果 d[i] = x 那么 对于任意 x'>x 且 x'%a[1]=i 都可以组合 (x'与x相差整数个a[1],差几倍补几倍就行了)

而 对任意 x'<x 且 x'%a[1]=i 都到达不了 所以 不能到达的数为 x/a[1];

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define maxn 110
using namespace std;
typedef long long ll;
int mp[maxn][maxn];
int d[maxn];
int v[maxn];
int a[maxn];
const int inf = 0x3fffffff;
int main()
{
    for(int i = 0 ; i<maxn ; i++){
        for(int j = 0 ; j<maxn ; j++){
            mp[i][j]=inf;
        }
        mp[i][i]=0;
    }
    int n;
    scanf("%d",&n);
    int minn=0;
    for(int i = 0 ; i<n ; i++){
        scanf("%d",&a[i]);
    }
    sort(a,a+n);
    for(int i = 0 ; i<n ; i++){
        for(int j = 0 ; j<a[0]; j++){
            mp[j][(j+a[i])%a[0]]=min(mp[j][(j+a[i])%a[0]],a[i]);
        }
    }
    for(int i = 0 ; i<a[0] ; i++){
        d[i]=inf;
    }
    mp[0][0]=0;
    d[0]=0;
    for(int i = 0 ; i<a[0] ; i++){
        int minn = inf;
        int k = 0;
        for(int j = 0 ; j<a[0] ; j++){
            if(d[j]<minn&&!v[j]){
                minn=d[j];
                k=j;
            }
        }
        v[k]=1;
        for(int j = 0 ; j<a[0] ; j++){
            if(d[j]>d[k]+mp[k][j]){
                d[j]=d[k]+mp[k][j];
            }
        }
    }
    for(int i = 0 ; i<a[0] ; i++){
        if(d[i]>=inf){
            printf("INF\n");
            return 0;
        }
    }
    ll sum = 0;
    for(int i = 0 ; i<a[0] ; i++){
        sum += d[i]/a[0];
    }
    printf("%d\n",sum);
    return 0;
}
0s ac
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值