首页 > 其他分享 >CF118C-Fancy-Number题解

CF118C-Fancy-Number题解

时间:2023-03-01 16:11:19浏览次数:52  
标签:CF118C tmp string int 题解 Number ww ++ 花费

题目传送门

题意:有一个 \(n\) 位的数字串,每位为 \(0-9\)。每次操作可以更改一位的数字,代价为新旧两位数字之差。问使字符串存在某一个数的出现次数超过 \(k\) 的最小代价。如果有多种方案,输出字典序最小的。

求最小代价非常简单,枚举要让哪个数出现超过 \(k\) 次,然后将所有位求出将这一位改成该数的代价,排个序前 \(k\) 小值即可。难点在于如何让字典序最小。

做法一

考虑排序时不仅要求代价从小到大,对于同样代价的两位,要么是原来比较大,减小为要求的数,要么是原来比较小,增大为要求的数,此时优先改数字大的那位,因为改完可以变小,字典序一定减小。如果两位相同,考虑是增大还是减小,如果减小则越靠前越好,如果增大则越靠后越好。

By cxm1024

#include<bits/stdc++.h>
using namespace std;
signed main() {
	int n,k,ans=1e9;
	string s,t;
	cin>>n>>k>>s;
	for(char i='0';i<='9';i++) {
		vector<pair<int,int> > dis;
		for(int j=0;j<s.size();j++)
			dis.push_back({abs(s[j]-i),j});
		sort(dis.begin(),dis.end(),[&](pair<int,int> p,pair<int,int> q) {
			if(p.first!=q.first) return p.first<q.first;
			if(s[p.second]!=s[q.second]) return s[p.second]>s[q.second];
			if(s[p.second]>i) return p.second<q.second;
			else return p.second>q.second;
		});
		int res=0;
		string tmp=s;
		for(int j=0;j<k;j++)
			res+=dis[j].first,tmp[dis[j].second]=i;
		if(res<ans||(res==ans&&tmp<t))
			ans=res,t=tmp;
	}
	cout<<ans<<endl<<t<<endl;
	return 0;
}

做法二

考虑小于最大花费的一定全选,所以字典序只在等于最大花费的位之间有影响。对于等于最大花费的位,只需要先正着处理比要变成的数大的,再反着处理比它小的(原因同做法一),避免了复杂的比较函数。

By tomekkulczynski

char s[11013], tmp[11013];

int main()
{
    int n,k;
    scanf("%d %d %s",&n,&k,s);
    int ret = 9 * n + 1;
    string w = "";
    REP(c, 10) {
        VI t;
        REP(i, n) t.PB(abs(s[i] - '0' - c));
        sort(ALL(t));
        int  ss = 0;
        REP(i, k) ss += t[i];
        if(ss < ret) ret = ss, w = "a";
        if(ss == ret) {
            REP(i, n+1) tmp[i] = s[i];
            int d = t[k-1], ww = 0;
            REP(i, n) if(abs(s[i] - '0' - c) < d) tmp[i] = c + '0', ww++;
            REP(i, n) if(ww < k && s[i] == c + '0' + d) tmp[i] = c + '0', ww++;
            FORD(i, n-1, 0) if(ww < k && s[i] == c + '0' - d) tmp[i] = c+'0', ww++;
            w = min(w, (string)tmp);
        }
    }
    printf("%d\n%s\n",ret, w.c_str());
    return 0;
}

做法三

考虑从每位花费的角度枚举。由于一定是先选花费小的,再选花费大的,于是从小到大枚举花费,对于这个花费,有可能是变小的花费,也有可能是变大的花费,此时先正着处理变小,再反着处理变大,直至取足数量即可。

By watashi

#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
	int n, k, m, s;
	string pat, tmp;
	pair<int, string> ans = make_pair(1000000007, "");

	cin >> n >> k;
	cin >> pat;
	for (int d = 0; d < 10; ++d) {
		s = 0;
		tmp = pat;
		m = count(tmp.begin(), tmp.end(), '0' + d);
		for (int x = 1; x < 10; ++x) {
			if (d + x < 10) {
				char y = '0' + (d + x);
				for (int i = 0; m < k && i < n; ++i) {
					if (tmp[i] == y) {
						tmp[i] = '0' + d;
						s += x;
						++m;
					}
				}
			}
			if (0 <= d - x) {
				char y = '0' + (d - x);
				for (int i = n - 1; m < k && i >= 0; --i) {
					if (tmp[i] == y) {
						tmp[i] = '0' + d;
						s += x;
						++m;
					}
				}
			}
		}
		ans = min(ans, make_pair(s, tmp));
	}
	cout << ans.first << endl << ans.second << endl;

	return 0;
}

做法四

可以使用类似搜索的思路来递归计算。原理类似做法三,每次扩张一格距离,依次处理,使用递归来简化代码。每次判断当前小还是大,递归时跳到对面处理,直到取足数量。

By Komaki

using namespace std;
#define li       	long long int
#define rep(i,to)	for(li i=0;i<((li)(to));++i)
#define pb       	push_back
#define sz(v)    	((li)(v).size())
#define bit(n)   	(1ll<<(li)(n))

li get(char c){
	return c-'0';
}
string str;
li cal(li rem,li to,li now){
	li res=0;
	if(to<now){
		if(now<=9){
			for(li i=0;i<sz(str);i++)if(get(str[i])==now){
				str[i]=to+'0';
				rem--;
				res+=abs(now-to);
				if(rem==0) return res;
			}
		}
		res+=cal(rem,to,to-(now-to));
	}else if(now<to){
		if(0<=now){
			for(li i=sz(str)-1;0<=i;i--)if(get(str[i])==now){
				str[i]=to+'0';
				rem--;
				res+=abs(now-to);
				if(rem==0) return res;
			}
		}
		res+=cal(rem,to,to+(to-now)+1);
	}else{
		rep(i,sz(str))if(get(str[i])==now) rem--;
		if(rem<=0) return res;
		res+=cal(rem,to,to+1);
	}
	return res;
}

int main(){
	li n,k;
	string base;
	cin>>n>>k>>base;
	li best=bit(60);
	string ans="";
	rep(i,10){
		str=base;
		li tmp=cal(k,i,i);
		if(tmp<best || (tmp==best && str<ans)){
			best=tmp;
			ans=str;
		}
	}
	cout<<best<<endl;
	cout<<ans<<enddl;
}

标签:CF118C,tmp,string,int,题解,Number,ww,++,花费
From: https://www.cnblogs.com/cxm1024/p/17168411.html

相关文章

  • CF15D-Map题解
    题目传送门题意:有一个\(n\timesm\)的矩阵,每个格子有一个权值。每次操作会选择一个\(x\timesy\)的矩形区域,花费为“每个位置的权值减去最小权值”之和,区域之间不能......
  • CF杂题题解
    129B.StudentsandShoelaces题意:一个\(n\)个点\(m\)条边的无向图,每一轮删去所有度数为\(1\)的点,问删几轮停止。暴力模拟每一轮即可,每次删点更新邻居度数。{%......
  • CSP-J2022-C-逻辑表达式题解
    题意:给你一个由0、1、&、|、(、)组成的字符串,保证是一个合法的逻辑表达式。其中括号优先级最高,与运算优先级高于或运算,同级之间从左到右算。定义一次短路为,或运算的左边......
  • Gym100078H-History-of-Football题解
    题目传送门题意:有\(n\)支球队,每两支球队之间都会进行一场较量,赢者积\(3\)分,输者积\(0\)分,如果平局则各积\(1\)分。给出每支球队的最终积分,求方案数。\(n\le8\)......
  • Gym100753D-Carpets题解
    题目传送门题意:有一个\(H\timesW\)的地板和\(n\)个矩形地毯,问是否能不重不漏地填满地板。\(H,W\le100,n\le7\)。考虑朴素的搜索,每次考虑最左上角的没填的位置,枚......
  • Gym100825C-KenKen-You-Do-It题解
    题目传送门题意:在一个矩阵上选中了若干个格子,保证连通。你需要在这些格子中填数,使得:每行每列不能重复,且这些数进行给定运算(可以认为只有加法和乘法)的结果为给定的数值。......
  • Gym100959I-Robots题解
    题意:平面直角坐标系上有\(n\)个机器人,每个机器人有一个上下左右之一的方向,初始所有机器人静止。在第\(0\)秒,你让第一个机器人开始朝它的方向移动,速度为每秒一个单位。......
  • Gym101252B-Kakuro题解
    题目传送门题意:有一个矩阵,每个格子为黑色或白色。你需要向白色格子中填\(1\sim9\)的整整睡。从某一个黑色格子的右侧往右连续的一段极长的白色格子被称为一个run,往下......
  • C#、IIS获取时间带星期或日期问题解决
    ,获取时间老是带星期,如:2018-8-8星期日12:00:00,造成写入数据库时无法识别为时间格式报错。试了修改区域语言和修改指定注册表均无效。   解决方法一:在代码里处理时......
  • SPOJ Query On A Tree IV 题解
    SPOJQueryOnATreeIV题解一个边分治套线段树套堆的题目比较难写但是有不小的启发思路来源和代码都抄自[SPOJ-QTREE4]QUERYONATREEIV题解|KSKUN'sBlog简......