UPC6616: Small Multiple(最短路 or 双端队列)

6616: Small Multiple

时间限制: 1 Sec  内存限制: 512 MB
提交: 407  解决: 60
[提交] [状态] [讨论版] [命题人:admin]

题目描述

Find the smallest possible sum of the digits in the decimal notation of a positive multiple of K.
Constraints
2≤K≤105
K is an integer.

输入

Input is given from Standard Input in the following format:
K

输出

Print the smallest possible sum of the digits in the decimal notation of a positive multiple of K.

扫描二维码关注公众号,回复: 2628003 查看本文章

样例输入

6

样例输出

3

提示

12=6×2 yields the smallest sum.

来源/分类

ABC077&ARC084 

题意:一个数字k,要求找到一个数为k的倍数使得其各位数之和加起来最小。

小结:思路是种很奇妙的东西,不可思议。

SPFA:

可以从 x = i ,0 \leq i < k,分别向X+1和X*10分别建立一条权值为1和0的有向单边,因为如果是考虑数位和的话,+1表示数位和+1,而*10则数位和是不变的。然后跑一遍最短路,答案就是1到k的倍数的最短路,在这里如果大于k我们要modk,这样1到0的最短路就是1到k的倍数的最短路就是答案。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5+10;
int head[maxn];
int vis[maxn];
ll dis[maxn];
int cnt;
struct edge{
	int u,v,w;
	int next;
}edge[maxn*2];
void add(int u,int v,int w)
{
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
void spfa(int sta)
{

    queue<int>Q;
    Q.push(sta);
    vis[sta]=1;
    dis[sta]=0;
    while(!Q.empty())
    {
        int now=Q.front();
        Q.pop();
        vis[now]=0;
        for(int i=head[now];i != -1;i=edge[i].next)
        {
            int w=edge[i].w;
            int son=edge[i].v;
            if(dis[now]+w<dis[son]) 
            {
                    dis[son]=dis[now]+w;
                    if(!vis[son])
                    {
                        Q.push(son); 
                        vis[son]=1;
                    }
            }

        }
    }
}
int main()
{
	int k;
	cnt = 0;
	scanf("%d",&k);
	memset(head,-1,sizeof(head));
	for(int i=0;i <= k;i++)
    {
        vis[i]=0;
        dis[i]=1e16+7;
    }
    
	for(int i = 0;i < k;i++)
	{
		add(i,(i+1)%k,1);
	//	cout<<i<<" "<<(i+1)%k<<endl;
		add(i,(i*10)%k,0);
	//	cout<<i<<" "<<(i*10)%k<<endl;
	}
	spfa(1);
	printf("%lld\n",dis[0]+1);
}

双端队列deque

第一次接触双端队列,这种队列既可以当栈也可以当队列用,但是在实际应用中用的并不如队列跟栈多。它即可已从头操作也可以从尾部操作,包括了队列和栈的所有性质。

本题中我们有限考虑因为x*10大代价是0,所以肯定会优先考虑所以就需要在头部插入x*10在尾部插入x+1,这样可以保证一直最小代价在最前面,那么访问到0(k的倍数)就表示当前是最小的代价输出即可。

deque <int> q

q.front()//从头部取元素    q.back()//从尾部取元素

q.pop_front()//头部出一个  q.pop_back//尾部出一个

q.push_front()//头部插入 q.push_back()//尾部插入

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define IO ios::sync_with_stdio(false),cin.tie(0)
#define FIN freopen("D://code//in.txt", "r", stdin)
#define ppr(i,x,n) for(int i = x;i <= n;i++)
#define rpp(i,n,x) for(int i = n;i >= x;i--)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

inline int read() {//读入挂
    int ret = 0, c, f = 1;
    for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
    if(c == '-') f = -1, c = getchar();
    for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if(f < 0) ret = -ret;
    return ret;
}
typedef pair<int , int> P;//第一个表示mod k的的大小0就是k的倍数,第二个表示到这个状态的最短。 
bool vis[maxn];
int main()
{
	IO;
	int k;cin>>k;
	memset(vis,false,sizeof(vis));
	deque <P> Q;
	Q.push_back(make_pair(1,1));//初始1,1压入
	while(!Q.empty())
	{
		P x = Q.front(); Q.pop_front();
		if(vis[x.first]) continue;//当前状态之前已经达到过
		vis[x.first] = true;
		if(x.first == 0){printf("%d\n",x.second);break;}//找到最小的k的倍数的数位和
		int first = x.first,second = x.second;
		Q.push_front(make_pair(first*10%k,second));
		Q.push_back(make_pair(first+1%k,second+1)); 
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Pandapan1997/article/details/81407093