zoukankan      html  css  js  c++  java
  • CJOJ 1071 【Uva】硬币问题(动态规划)

    CJOJ 1071 【Uva】硬币问题(动态规划)

    Description

    有n种硬币,面值分别为v1, v2, ..., vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。

    Input

    第一行两个整数,n,S(1≤n≤100, 0≤S≤100000)。
    第二行n个整数vi-1...n(1≤vi≤S)。

    Output

    第一行两个整数,分别表示硬币数目的最小值 a 和最大值 b 。无解则输出 -1 。
    第二行 a 个整数分别表示使用的是第几种硬币。
    第三行 b 个整数分别表示使用的是第几种硬币。

    Sample Input

    6 12
    1 2 3 4 5 6

    Sample Output

    2 12
    6 6
    1 1 1 1 1 1 1 1 1 1 1 1

    Http

    CJOJ:http://oj.changjun.com.cn/problem/detail/pid/1071

    Source

    动态规划

    解决思路

    看到这道题首先想到的是spfa算法。令每一个面值为一个点,若面值j=i+v[k]则连一条边(只是这么想,实际不用连边),那么分别用spfa跑出0->S的最短路径和最长路径。

    这么想虽然没错,但会超时(70分)。下面会给出spfa代码,这里就不过多叙述。

    那么正解是什么呢?动态规划

    我们以求最小值为例,定义Dmin[i]表示能凑出面值i的最少硬币数,用path_min[i]记录这个数量是从哪个面值转移过来的。那么我们就有Dmin[i]=min(Dmin[i-V[j]]+1)
    最后求路径的时候先令一个变量k=S,每次将k-path_min[k]压入一个vector中,再将k=path_min[k],直到k==0。然后将vector排序,再输出。

    而最大值的操作是类似的。

    另外要注意的是,题中输出的应该是面值的编号,所以用一个Map[i]存下面值i对应的编号,所以压入vector的就是Map[k-path_min[k]]

    代码

    spfa(TLE代码)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    using namespace std;
    
    const int maxN=200;
    const int maxS=100050;
    const int inf=2147483647;
    
    int n,S;
    int V[maxN];
    int Dmax[maxS];
    int Dmin[maxS];
    int path_max[maxS];//记路径
    int path_min[maxS];
    bool vis[maxS];
    int Map[maxS];//因为最后要输出的是编号,所以为每一个面值都存一个编号
    queue<int> Q;
    vector<int> arr;
    
    void spfa_min();
    void spfa_max();
    void dfs_min(int sum);//dfs求答案,但会爆栈
    void dfs_max(int sum);
    
    int main()
    {
        memset(Dmax,-1,sizeof(Dmax));
        memset(Dmin,-1,sizeof(Dmin));
        memset(Map,-1,sizeof(Map));
        cin>>n>>S;
        for (int i=1;i<=n;i++)
        {
            cin>>V[i];
            if (Map[V[i]]==-1)
                Map[V[i]]=i;//记录面值i所对应的的编号i
        }
        spfa_min();
        spfa_max();
        cout<<Dmin[S]<<' '<<Dmax[S]<<endl;
        //dfs_min(S);cout<<endl;
        //dfs_max(S);
    
        int k=S;//手动写栈,排序
        while (k>0)
        {
            arr.push_back(Map[k-path_min[k]]);
            k=path_min[k];
        }
        sort(arr.begin(),arr.end());
        for (int i=0;i<arr.size();i++)
            cout<<arr[i]<<' ';
        cout<<endl;
    
        k=S;//手动写栈,排序
        arr.clear();
        while (k>0)
        {
            //cout<<":AA"<<endl;
            arr.push_back(Map[k-path_max[k]]);
            k=path_max[k];
        }
        sort(arr.begin(),arr.end());
    
        for (int i=0;i<arr.size();i++)
            cout<<arr[i]<<' ';
        cout<<endl;
        return 0;
    }
    
    void spfa_min()
    {
        memset(vis,0,sizeof(vis));
        Q.push(0);
        vis[0]=1;
        Dmin[0]=0;
        do
        {
            int u=Q.front();
            Q.pop();
            vis[u]=0;
    
            for (int i=1;i<=n;i++)
                if (u+V[i]<=S)
                if ((Dmin[u]+1<Dmin[u+V[i]])||(Dmin[u+V[i]]==-1))
                {
                    Dmin[u+V[i]]=Dmin[u]+1;
                    path_min[u+V[i]]=u;
                    if (vis[u+V[i]]==0)
                    {
                        Q.push(u+V[i]);
                        vis[u+V[i]]=1;
                    }
                }
        }
        while (!Q.empty());
        return;
    }
    
    void spfa_max()
    {
        memset(vis,0,sizeof(vis));
        while (!Q.empty())
            Q.pop();
        Q.push(0);
        vis[0]=1;
        Dmax[0]=0;
    
        do
        {
            int u=Q.front();
            Q.pop();
            vis[u]=0;
    
            for (int i=1;i<=n;i++)
                if (u+V[i]<=S)
                if ((Dmax[u]+1>Dmax[u+V[i]])||(Dmax[u+V[i]]==-1))
                {
                    Dmax[u+V[i]]=Dmax[u]+1;
                    path_max[u+V[i]]=u;
                    if (vis[u+V[i]]==0)
                    {
                        vis[u+V[i]]=1;
                        Q.push(u+V[i]);
                    }
                }
        }
        while (!Q.empty());
        return;
    }
    
    void dfs_min(int sum)
    {
        if (sum==0)
            return;
        dfs_min(path_min[sum]);
        cout<<Map[sum-path_min[sum]]<<' ';
        return;
    }
    
    void dfs_max(int sum)
    {
        if (sum==0)
            return;
        dfs_max(path_max[sum]);
        cout<<Map[sum-path_max[sum]]<<' ';
        return;
    }
    

    动态规划

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    const int maxN=200;
    const int maxS=200000;
    const int inf=1000000000;
    
    int n,S;
    int V[maxN];
    int Dmin[maxS];
    int Dmax[maxS];
    int path_min[maxS];
    int path_max[maxS];
    int Map[maxS];
    vector<int> arr;//输出时要临时存放面值的编号并排序
    
    int main()
    {
        memset(Map,-1,sizeof(Map));
        cin>>n>>S;
        for (int i=1;i<=n;i++)
        {
            cin>>V[i];
            if (Map[V[i]]==-1)
                Map[V[i]]=i;
    
        }
        for (int i=1;i<=S;i++)
        {
            Dmin[i]=inf;//初始值,最小值最大,最大值最小
            Dmax[i]=-inf;
        }
        Dmin[0]=0;
        Dmax[0]=0;
        for (int i=1;i<=S;i++)
        {
            for (int j=1;j<=n;j++)
                if (i-V[j]<0)
                    continue;
                else//DP
                {
                    if (Dmin[i]>Dmin[i-V[j]]+1)
                    {
                        Dmin[i]=Dmin[i-V[j]]+1;
                        path_min[i]=i-V[j];
                    }
                    if (Dmax[i]<Dmax[i-V[j]]+1)
                    {
                        Dmax[i]=Dmax[i-V[j]]+1;
                        path_max[i]=i-V[j];
                    }
                }
        }
    
        cout<<Dmin[S]<<' '<<Dmax[S]<<endl;
    
        int k=S;
        arr.clear();
        while (k>0)//输出方案
        {
            arr.push_back(Map[k-path_min[k]]);
            k=path_min[k];
        }
        sort(arr.begin(),arr.end());
        for (int i=0;i<arr.size();i++)
            cout<<arr[i]<<' ';
        cout<<endl;
    
        k=S;
        arr.clear();
        while (k>0)
        {
            arr.push_back(Map[k-path_max[k]]);
            k=path_max[k];
        }
        sort(arr.begin(),arr.end());
        for (int i=0;i<arr.size();i++)
            cout<<arr[i]<<' ';
        cout<<endl;
        return 0;
    }
    
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    解决PKIX:unable to find valid certification path to requested target 的问题
    Linux 上的常用文件传输方式介绍与比较
    用VNC远程图形化连接Linux桌面的配置方法
    红帽中出现”This system is not registered with RHN”的解决方案
    linux安装时出现your cpu does not support long mode的解决方法
    CentOS SSH配置
    es6扩展运算符及rest运算符总结
    es6解构赋值总结
    tortoisegit安装、clon、推送
    es6环境搭建
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7137823.html
Copyright © 2011-2022 走看看