2021/01/18训练总结

前言

昨天晚上和队友打了一场GYM[链接],看了的题目里面还有A&M没有写出来,所以今天先将这两题补完

M. Mathematics society problem

题目链接:M. Mathematics society problem
题目大意:给定一个只含有1-9的数字串,要求1-9分别删去 n u m [ i ] num[i] num[i]个,要求剩下的数字串表示的数字最大。
在这里插入图片描述

数据范围: ∣ s ∣ ≤ 1000 |s|\le1000 s1000
题解:昨晚想了一个假算法,先说说假算法,我们考虑从 1 − > 9 1->9 1>9开始删去,如果当前要求删去 i i i,然后我们遍历到了一个位置刚好是 i i i,那么这个位置后面的数字比 i i i大,我们就删去这个 i i i,否则留下来,最后再从n->1将多出来的 i i i删去。我们使用链表来实现这个模拟过程。
代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
char s[N];
int n;
int num[20],w[N],r[N],l[N],vis[N]; 
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = 1; i <= 9; i++)read(num[i]);
	for (int i = 1; i <= n; i++)w[i] = s[i] - '0';
	for (int i = 0; i <= n; i++)
	{
		r[i] = i + 1;
		l[i] = i - 1;
	}
	for (int i = 1; i <= 9; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (!num[i])break;
			if (w[j] != i||vis[j])continue;
			if (w[r[j]] > i)
			{
				r[l[j]] = r[j];
				l[r[j]] = l[j];
				num[i]--;
				vis[j] = 1;
			}
		}
		if (!num[i])continue;
		for (int j = n; j >= 1; j--)
		{
			if (!num[i])break;
			if (w[j] != i || vis[j])continue;
			r[l[j]] = r[j];
			l[r[j]] = l[j];
			vis[j] = 1;
			num[i]--;
		}
	}
	int st = r[0];
	if (st > n)printf("0");
	while (st <= n)printf("%d", w[st]), st = r[st];
	
	return 0;
}

很可惜,WA17了。我们看看数据:

96949171972825919472
3 2 0 1 1 0 2 1 3

答案是:9699742,而我们的输出是:9699472
我简单的模拟了一下
在这里插入图片描述
可以发现当在删去7时,前面的7被删去了而后面的7被保留了下来,因为后面接的是数字9。但这个数字9在最后被删去了,根本没有替换掉7!所以这个贪心策略无法保证我们后面的数字一定可以去替换前面更小的数字。
正解:我们考虑用一个双向队列去模拟,即双向队列里面装的就是我们的最终答案,我们想要让最后的数字串表示的数字大,就要让大数字尽可能的放在前面,这里其实就直接模拟就行。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 200000 + 10;
const int mod = 1e9 + 7;


char s[N];
int num[20],re[20];
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	scanf("%s", s + 1);
	for (int i = 1; i <= 9; i++)read(num[i]);//要求删去的个数 
	deque<int>p;
	int n = strlen(s + 1);
	for (int i = 1; i <= n; i++)re[s[i] - '0']++;//每个数字剩余的个数 
	for (int i = 1; i <= n; i++)
	{
		int now = s[i] -'0';
		if (re[now] == num[now])//如果要求删去的个数等于剩余的个数,就只能删去了 
		{
			re[now]--; num[now]--;
			continue;
		}
		while (p.size() && num[p.back()] && now > p.back())//如果最后一位数字可以删去,并且当前数字更大,就删去 
		{
			num[p.back()]--;
			p.pop_back();
		}
		p.push_back(now);
		re[now]--;
	}
	for (auto it : p)printf("%d", it);
	return 0;
}

A. Acing the contest

题目链接:A. Acing the contest
题目大意:有n个人,p道题,每一个有 w [ i ] w[i] w[i]的能量值,解决第i道题需要 d [ i ] d[i] d[i]能力值,奖励 s [ i ] s[i] s[i]得分,一个人只能上场一次,问如何安排答题顺序可以使得得分最高。(题目顺序不可变,题目可以跳过)
数据范围: 1 ≤ n ≤ 10 1\le n \le 10 1n10 1 ≤ p , w [ i ] , s [ i ] , d [ i ] ≤ 100 1\le p,w[i],s[i],d[i] \le 100 1p,w[i],s[i],d[i]100
题解:昨天写这题的时候,总想着用全排列啥操作来决定上场顺序。。。然后就完全跑偏了。。。其实这个数据量的话除了搜索之外,一定一定要想到状压 d p dp dp,想到了状压,其实解决这个问题就不难了。我们用 d p [ i d ] [ r e w ] [ s t a t e ] dp[id][rew][state] dp[id][rew][state]来表示,在第id道题,当前上场的人剩余的能量,上场的人注册的状态下的最高得分。转移也可以很快写出来,具体看下代码就明白了。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 200 + 10;
const int mod = 1e9 + 7;
int w[N], n, p, d[N], s[N];
int dp[110][105][1 << 11];
int dfs(int id, int rew, int state)//第id道题,还有rew个能量,答题情况的压缩表示为state
{
	if (id == p)return 0;
	if (dp[id][rew][state] != -1)return dp[id][rew][state];
	int ans = 0;
	ans = max(ans,dfs(id + 1, rew, state));//这题跳过
	if (rew >= d[id])ans = max(ans, s[id] + dfs(id + 1, rew - d[id], state));//不换人继续写
	for (int i = 0; i < n; i++)
	{
		if ((state & (1 << i)) == 0)//这个人还没答过题
		{
			ans = max(ans, dfs(id, w[i], state | (1 << i)));
		}
	}
	return dp[id][rew][state] = ans;
}
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	read(n), read(p);
	for (int i = 0; i < n; i++)read(w[i]);
	for (int i = 0; i < p; i++)read(d[i]);
	for (int i = 0; i < p; i++)read(s[i]);
	memset(dp, -1, sizeof(dp));
	printf("%d\n", dfs(0, 0, 0));

	return 0;
}

P4124 [CQOI2016]手机号码

题目链接:P4124 [CQOI2016]手机号码
题目大意:
在这里插入图片描述
数据范围: 1 0 10 ≤ L ≤ R ≤ 1 0 11 10^{10}\le L \le R \le10^{11} 1010LR1011
题解:数位dp感觉多写几题就能发现都是一个套路了,就是记忆化搜索,这里我们把所有要维护的东西都丢进参数里面。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
#define int long long
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
int dp[15][2][11][11][2][2][2];
int w[20];
int dfs(int cur, int limit, int pre1, int pre2, bool isok, bool _4, bool _8)
{
	if (_4 && _8)return 0;
	if (!cur)return isok;
	if (~dp[cur][limit][pre1][pre2][isok][_4][_8])return dp[cur][limit][pre1][pre2][isok][_4][_8];
	int tp = limit ? w[cur] : 9;
	int ans = 0;
	for (int i = 0; i <= tp; i++)
	{
		ans += dfs(cur - 1, limit && (i == tp), i, pre1, isok || (i == pre1 && i == pre2), _4 || (i == 4), _8 || (i == 8));
	}
	return dp[cur][limit][pre1][pre2][isok][_4][_8] = ans;
}
int find(int x)
{
	w[0] = 0;
	while (x)w[++w[0]] = x % 10, x /= 10;
	if (w[0] != 11)return 0;
	memset(dp, -1, sizeof(dp));
	int ans = 0;
	for (int i = 1; i <= w[w[0]]; i++)
	{
		ans += dfs(w[0] - 1, i == w[w[0]], i, 0, 0, i == 4, i == 8);
	}
	return ans;
}
signed main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	ll l, r;
	read(l), read(r);
	printf("%lld\n", find(r) - find(l - 1));

	return 0;
}

P4999 烦人的数学作业

题目链接:P4999 烦人的数学作业
题目大意:给出一个区间 [ L , R ] [L,R] [L,R],求L到R区间内每个数的数字和
数据范围: 1 ≤ L ≤ R ≤ 1 0 18 , 1 ≤ T ≤ 20 1 \leq L \leq R \leq 10^{18},1 \leq T \le20 1LR1018,1T20
题解:数位 d p dp dp模板题,非常经典。建议数位 d p dp dp入门可以从这个题开始。直接记忆化搜索写就行

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
#define int long long
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
int w[20],dp[25][2][9*18+5];
int t;
int dfs(int cur, int limit, int sum)
{
	if (!cur)return sum;
	if (~dp[cur][limit][sum])return dp[cur][limit][sum];
	int tp = limit ?  w[cur]:9;
	int ans = 0;
	for (int i = 0; i <= tp; i++)
	{
		ans += dfs(cur - 1, limit && (i == tp), sum + i);
	}
	return dp[cur][limit][sum] = (ans % mod);
}
int find(int x)
{
	w[0] = 0;
	while (x)w[++w[0]] = x % 10, x /= 10;
	memset(dp, -1, sizeof(dp));
	return dfs(w[0], 1, 0)%mod;
}
signed main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	read(t);
	
	while (t--)
	{
		int l, r;
		read(l), read(r);
		printf("%lld\n", ((find(r) - find(l-1)) % mod + mod)%mod);
	}
	return 0;
}

P6218 [USACO06NOV] Round Numbers S

题目链接:P6218 [USACO06NOV] Round Numbers S
题目大意:
在这里插入图片描述
数据范围: l ≤ l ≤ r ≤ 2 ∗ 1 0 9 l \le l \le r \le 2*10^9 llr2109
题解:又是板子题,代码就是上一题的稍微改了一下。

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
#define int long long
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
int w[50],dp[35][2][35][35][2];
int t;
int dfs(int cur, int limit, int sum0,int sum1,int is0)
{
	if (!cur)return sum1<=sum0;
	if (~dp[cur][limit][sum0][sum1][is0])return dp[cur][limit][sum0][sum1][is0];
	int tp = limit ?  w[cur]:1;
	int ans = 0;
	for (int i = 0; i <= tp; i++)
	{
		ans += dfs(cur - 1, limit && (i == tp), is0? 0:sum0 +(i == 0), sum1 + (i == 1), is0 && (i == 0));
	}
	return dp[cur][limit][sum0][sum1][is0] = ans;
}
int find(int x)
{
	if (x == 0)return 0;
	w[0] = 0;
	while (x)w[++w[0]] = x&1, x/=2;
	memset(dp, -1, sizeof(dp));
	return dfs(w[0], 1, 0,0,1)-1;
}
signed main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	int l, r;
	read(l), read(r);
	printf("%lld\n", find(r) - find(l-1));
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值