【一道来自老师的题的题解】equip——奇妙的最短路

这道题真的第一眼完全想不到是最短路啊!!!!!!!!

感谢DR大佬讲解!!!!!90°鞠躬 =u=

 暂时没有评测网址,(因为需要special judge)敬请期待

机房另一大佬JYY题解,可以对比参照(说不定就会了呢)<-点这个

(她的博客里有一些问题答疑不懂得去看看哈)


 

题面:

 

 

 分析过程依旧来源于老师

 

 为什么可以连图的证明过程忽略(因为我不会

这张图基本就是所有能被表示的数,求出的最短路就是最小能被表示的数,由于它们都对 a[1] 取模,根据这点可以用已求出的数据计算出每种碎片的使用情况,(我们默认能多用 a[1] 就多用)这题是“任意一种方案即可”,所以我们不用考虑“用 n 个 a[ j ] 可以表示 a[ i ] ”的情况

参考代码可能会便于理解思路(代码有超级超级详细的注释哦真的不看看嘛)

/*数组变量解释:
 dis 存放最短路值,pre 存放每个价值所用的碎片编号, vis , sum 代码中都有详细解释
*/
#include<bits/stdc++.h>
#define A 20001
#define N 5001
using namespace std;
typedef long long ll;
struct node 
{
    int x; ll v;
    bool operator < (const node &oth) const 
    {
        return v > oth.v;
    }//重载运算符实现大根堆 
};
priority_queue<node>q;
int pre[A], n, m, K, vis[A];
ll a[N], dis[A], sum[N];
ll getin() 
{
    ll s = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c <= '9' && c >= '0') s = s * 10ll + c - '0', c = getchar();
    return s;
}//快读忽略 
void dijkstra() 
{
    for (int i = 1; i < a[1]; i++) dis[i] = 1e18;
    dis[0] = 0;
    q.push((node) {0, 0});
    //赋初值 
    while (!q.empty())
    {
        int u = q.top().x;//这是当前的点 
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        // vis数组用来判断是否更新 
        //参考dijsktra 
        for (int i = 1; i <= n; i++)
         {
            int v = (u + a[i]) % a[1];//目前的需要更新的点 
            if (dis[u] + a[i] < dis[v])
            {
                dis[v] = dis[u] + a[i];//判断是否需要更新 
                pre[v] = i; //这个正在更新的点边权(a[j]) 的编号 J 
                q.push((node){v, dis[v]});//加入堆 
            }
        }
    }
}
int main() {
    //freopen("equip.in", "r", stdin);
    //freopen("equip.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &K);
    for (int i = 1; i <= n; i++) a[i] = getin();
    dijkstra();
    for (int i = 1; i <= m; i++) {
        ll x = getin();
        if (x < dis[x % a[1]]) printf("No\n");//判断无法被兑换的情况 
        else {
            printf("Yes");
            if (K == 1) {
                for (int j = 1; j <= n; j++) sum[j] = 0;
                //sum[j] 表示的是每种碎片需要的数量 
                sum[1] += (x - dis[x % a[1]]) / a[1];
                //需要的 a[1] 碎片数量 
                // x-dis[x%a[1]] 是刨掉a[1]以外碎片使用的总价值 
                while (x % a[1]) 
                {
                    sum[pre[x % a[1]]]++;//这个位置的碎片数++; 
                    x = ((x - a[pre[x % a[1]]]) % a[1] + a[1]) % a[1];
                    //把当前的碎片和a[1]刨掉剩下的需要价值(那么长主要是保证其精度不要在意) 
                }
                for (int j = 1; j <= n; j++) printf(" %I64d", sum[j]);
                //简简单单的输出,不用的话就是输出0 (反正任意一种情况都能过2333) 
            }
            printf("\n");
        }
    }
    return 18751214;//防我自己抄袭 
}

 

好的就是这些,由于目前没找着原题,所以无法评测,看不懂可以找我

ありがとうございます

posted @ 2019-10-18 11:33  莳萝萝  阅读(197)  评论(0编辑  收藏  举报