2022-03-03每日刷题打卡

2022-03-03每日刷题打卡

力扣——每日一题

258. 各位相加

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。

示例 1:

输入: num = 38
输出: 2
解释: 各位相加的过程为:
38 --> 3 + 8 --> 11
11 --> 1 + 1 --> 2
由于 2 是一位数,所以返回 2。

提示:

  • 0 <= num <= 2^31 - 1

**进阶:**你可以不使用循环或者递归,在 O(1) 时间复杂度内解决这个问题吗?

非进阶做法:两个while嵌套,内部的while每次获取num各个位上的数并加起来,如果加起来后的数大于等于10,就继续回头循环。外面那层while就是判断num是否是一位数。

class Solution {
public:
    int addDigits(int num) {
        int res=0;
        while(num)
        {
            while(num)
            {
                res+=num%10;
                num/=10;
            }
            if(res>=10)num=res,res=0;
        }
        return res;
    }
};

进阶做法:首先我们知道,对于这样一个数abcd,它等于a*10^3+b *10^2+c 10^1+d 10^0。我们要获得的是各位数之和,即a+b+c+d,两者之间的差值就是a 999+b * 99+c9,此时可以发现,差值都是9的倍数,即对9取模后等于0。既然知道差值对9取余会变成0,那我们不如就直接对num%9,这样就变成(a+999 * a+b+99 * b+9 c+d)%9,可以化为(a%9+(999 * a)%9+b%9+(b 99)%9+c%9+(c *9)%9+d%9)%9,这样得到的就是a+b+c+d了。而且不用担心会大于等于10,因为我们计算总和后我们仍然会对他进行取模。要注意的是,有1点特殊情况,如果遇到9的倍数,比如9,不能对它直接取模9,因为9它已经是一位数了,如果取模得到的就是0,所以要特殊处理一下 ,当num为9的倍数时直接返回9。

class Solution {
public:
    int addDigits(int num) {
        if(num%9==0&&num)return 9;
        return num%9;
    }
};

代码源——div1每日一题

序列操作 - 题目 - Daimayuan Online Judge

给定一个长度为 n 的序列 a1,a2,…,an。

你需要进行两种操作:

1、1 x y——将第 x 个数变为 y;

2、2 y——将所有小于 y 的数修改为 y;

共执行 q 次操作,输出执行完所有操作后的序列。

输入格式

第一行两个数字 n , q (1≤n,q≤10^6)。

接下来一行 n 个整数 a1,a2,…,ana (0≤a≤10^9)。

接下来 q 行,每行表示一个操作: 1 x y 或 2 y (1≤x≤n,0≤y≤10^9)。

输出格式

一行整数,表示操作完后的序列,用空格分隔。

样例输入
5 5
3 6 14 16 12
2 13
2 16
1 1 1
1 2 14
2 11
样例输出
11 14 16 16 16

用一个数组v把元素都存下来,首先我们看2操作,它是把所有小于y的数都变成y,那么我们其实只用看所有2操作中最大的那个y就可以了,不管先后,我们只要记录最大的那个y,等所有操作结束后遍历数组,把小于y的变成y即可。其次是操作1,操作1有点问题,因为1操作是强制修改,所以哪怕已经被二操作改成y了,我还是可以通过1操作把值改成较小的,比如2 16,1 1 1,这样下来第一个数还是1,但要是根据我们前面说的,记录最大的y等所有操作结束后再变,这样会使得第一个数变成16,为解决这个问题我们要进行两步措施,一是准备一个时间数组times,用来记录每个数被操作1修改的时间(第几布操作被修改),二是我们把所以操作先记下来,然后从尾往头开始进行修改。从尾往头开始遍历,当遇到2操作时,判断这个2操作的y值是否比我们当前记录的要大(初始可以记录一个很小的数),如果是,就更新记录的值并把这个操作的时间记录下来。如果遇到1操作,则给数组v的元素进行修改,修改的值为当前1操作的y值和我们记录的最大值中的max,因为我们是从后往前计算的,如果是操作1的大,那么即时被修改了,后面的2操作里也没有能大过它的数,如果是操作2的大,那不管此时1操作修改成什么数了,后面的操作2也会把它修改。我们修改完后,在数组times里对应的位置上记录操作1的时间。我们每个位置只记录这一次时间,因为哪怕之和我们再遇到修改这个位置的操作1,那也是靠后面的操作1说的算,不管你前面修改的怎么样,最后也会被后面的修改。按照如上操作从尾往头遍历完所有操作后就可以开始输出数组了,如果将要输出的数小于我们记录的最大值,且时间也比我们最大值的操作小(如果这个位置没有被操作1修改过,时间记录为-1),那就把这个元素变成最大值后输出,如果时间比最大值的大,就原样输出。

(这题一开始交还超时了,后来把cout和cin改成scanf和printf就过了)

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>

typedef long long ll;

int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	vector<ll>v(n), times(n, -1);
	vector<vector<ll>>st;
	for (int i = 0; i < n; i++)scanf("%lld", &v[i]);
	for (int i = 0; i < m; i++)
	{
		int ans;
		scanf("%d", &ans);
		if (ans == 1)
		{
			vector<ll>v1(3);
			v1[0] = ans;
			scanf("%lld %lld", &v1[1], &v1[2]);
			st.push_back(v1);
		}
		else
		{
			vector<ll>v1(2);
			v1[0] = ans;
			scanf("%lld ", &v1[1]);
			st.push_back(v1);
		}
	}
	ll res = -1, time = -1;
	for (int i = m - 1; i >= 0; i--)
	{
		if (st[i][0] == 2 && res < st[i][1])
		{
			res = st[i][1];
			time = (long long)i;
		}
		else if (st[i][0] == 1 && times[st[i][1] - 1] == -1)
		{
			v[st[i][1] - 1] = max(st[i][2], res);
			times[st[i][1] - 1] = (long long)i;
		}
	}
	for (int i = 0; i < n; i++)
	{
		if (v[i] < res && time >= times[i])printf("%lld ", res);
		else printf("%lld ", v[i]);
	}
	return 0;
}
订单编号 - 题目 - Daimayuan Online Judge

小缘开了一家公司,生意很好,每天都会收到很多订单,自动交易系统会自动给这些订单生成没有重复的订单编号。但是有一天,系统出现了未知的错误,导致当天的订单编号可能有重复的,这可把小缘急坏了。你可以帮助小缘按照规则给这些订单重新编号吗?

按照时间先后顺序给出 N 个正整数作为原订单编号,你需要按照规则依次赋予这些订单新的编号,对于任意一个订单,要找到大于等于其原订单编号且未被使用过的(没有被之前的订单作为新的订单编号)的最小整数,作为它的新订单编号。

例如: 原订单编号依次为1 2 3 1,则新订单编号应该为1 2 3 4 (前3个订单的原订单编号都没有使用过,所以用其原订单编号即可,对于第四个订单,原订单编号为1,而1, 2, 3都已经被使用过,所以新订单编号为4)。

输入格式

第一行输入一个整数 N (1≤N≤5×10^5)

第二行输入 N 个数 ai (1≤ai≤10^9) 作为原订单编号。

输出格式

输出一行,包含 N 个整数为新的订单编号。

样例输入1
6
2 3 4 1 1 1
样例输出1
2 3 4 1 5 6

这题我们用个区间分割的方式来解,一开始准备一个区间,区间用数对pair表示,first是右区间,second是左区间(是的,反过来,至于为什么等下说),根据题目给的数据,我们可以开一个{2e9,1}的区间,把它存入set容器里,每次根据读入的编号来分割区间,比如样例第一个元素是2,我们就以2来分割左右区间,这样就可以分成{1,1}和{2e9,3}两个区间,为了快速找到编号所在的区间,我们可以用lower_found()函数来找,它的作用是找到数组里第一个不大于目标值的元素,用set并且把左右区间调换也是为了这一点,set的自动排序功能可以使得小区间永远排在前面,pair左右区间调换是为了把右区间较小的排在前面,set的排序遇到pair时,会先根据first来排序,如果相等就根据second排序。按照样例,输出完2后区间会分成{1,1}和{2e9,3},下一个数是3时就会变成{1,1}{2,3}{2e9,4},但{2,3}这一区间是非法的(别忘了前面的是右区间,右区间的数怎么能比左区间小呢)所以我们也要注意,如果分完后有区间出现左区间的数大于右区间,那就把这个数对删除。按照此情况继续输出完4和1后,set中区间只剩{2e9,5}了,此时读到编号为1,我们用lower_found找第一个不小于1的数,那就只能是这个{2e9,5}了,我们输出5,然后继续分割区间{2e9,6}……由此,便可以得到所有新的订单编号了。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>

typedef long long ll;
typedef pair<int, int>PII;

set<PII>s;

void myinsert(int l, int r)
{
	if (l > r)return;
	s.insert({ r,l });
}

int main()
{
	int n;
	cin >> n;
	s.insert({ 2e9,1 });
	for (int i = 1; i <= n; i++)
	{
		int x;
		scanf("%d",&x);
		auto it = s.lower_bound({ x,0 });
		if (it->second <= x)
		{
			cout << x << " ";
			myinsert(it->second, x - 1);
			myinsert(x + 1, it->first);
			s.erase(it);
		}
		else
		{
			cout << it->second << " ";
			myinsert(it->second + 1, it->first);
			s.erase(it);
		}
	}
	return 0;
}

一本通——动态规划

1306:最长公共子上升序列

【题目描述】

给定两个整数序列,写一个程序求它们的最长上升公共子序列。

当以下条件满足的时候,我们将长度NN的序列S1,S2,…,SN称为长度为M的序列A1,A2,…,AM的上升子序列:

存在1≤i1<i2<…<iN≤M,使得对所有1≤j≤N,均有Sj=Aij,且对于所有的1≤j<N,均有Sj<Sj+1。

【输入】

每个序列用两行表示,第一行是长度M(1≤M≤500),第二行是该序列的M个整数Ai(−231<=Ai<231)

【输出】

在第一行,输出两个序列的最长上升公共子序列的长度L。在第二行,输出该子序列。如果有不止一个符合条件的子序列,则输出任何一个即可。

【输入样例】

5 
1 4 2 5 -12 
4 
-12 1 2 4
【输出样例】
2 
1 4

先求两个数组的最长公共子序列,再求这个最长公共子序列的最长上升序列。

(我这写法有点乱,但能AC啊)

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>

typedef long long ll;
typedef pair<int, int>PII;

const int N = 550;
ll f[N][N], a[N], b[N];

int main()
{
	int n, m;
	cin >> n ;
	for (int i = 1; i <= n; i++)cin >> a[i];
	cin >> m;
	for (int i = 1; i <= m; i++)cin >> b[i];
	ll res = -1;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= n; j++)
		{
			f[i][j] = max(f[i - 1][j], f[i][j - 1]);
			if (a[i] == b[j])f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
			res = max(res, f[i][j]);
		}
	vector<ll>v;
	int l = n, r = m;
	while (res)
	{
		int x = -1, y = -1 ;
		for (int i = r; i > 0; i--)
		{
			if (f[l][i] != f[l][i - 1] && f[l][i] == res)
			{
				y = i;
			}
		}
		for (int i = l; i > 0; i--)
			if (f[i][y] != f[i - 1][y] && f[i][y] == res)
				x = i;
		v.push_back(b[y]);
		l = x - 1, r = y - 1;
		res--;
	}
	reverse(v.begin(), v.end());
	int len = v.size(),ans;
	vector<int>dp(len + 1, 1), h(len + 1,-1);
	for (int i = 0; i < v.size(); i++)
	{
		for (int j = i - 1; j >= 0; j--)
		{
			if (v[i] > v[j] && dp[i] < dp[j] + 1)
			{
				dp[i] = dp[j] + 1, h[i] = j;
			}
		}
		if (res < dp[i])
		{
			res = dp[i];
			ans = i;
		}
		if (h[i] == -1)h[i] = i;
	}
	cout << res << endl;
	vector<int>math;
	while (h[ans] != ans)
	{
		math.push_back(v[ans]);
		ans = h[ans];
	}
	math.push_back(v[ans]);
	for (int i = math.size()-1; i >=0 ; i--)cout << math[i] << " ";
	return 0;
}
1276:【例9.20】编辑距离

【题目描述】

设A和B是两个字符串。我们要用最少的字符操作次数,将字符串A转换为字符串B。这里所说的字符操作共有三种:

1、删除一个字符;

2、插入一个字符;

3、将一个字符改为另一个字符。

对任意的两个字符串A和B,计算出将字符串A变换为字符串B所用的最少字符操作次数。

【输入】

第一行为字符串A;第二行为字符串B;字符串A和B的长度均小于2000。

【输出】

只有一个正整数,为最少字符操作次数。

【输入样例】

sfdqxbw 
gfdgw
【输出样例】
4
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>

typedef long long ll;
typedef pair<int, int>PII;

const int N = 2010;
ll f[N][N];

int main()
{
	string str1, str2;
	cin >> str1 >> str2;
	int n = str1.size(), m = str2.size();
	for (int i = 0; i <= n; i++)f[i][0] = i;
	for (int i = 0; i <= m; i++)f[0][i] = i;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= m; j++)
		{
			f[i][j] = min(f[i - 1][j], f[i][j - 1])+1;
			f[i][j] = min(f[i][j], str1[i - 1] != str2[j - 1] ? f[i - 1][j - 1] + 1 : f[i - 1][j - 1]);
		}
	cout << f[n][m];
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值