NEUQ-ACM预备队-week14

107 饿饿饭饭

只看题目就可以确定 我们无法模拟食堂阿姨的每一轮打饭过程 如果一轮一轮给每一个等待吃饭的同学做自减操作 看起来会使TLE

那么就确定了

二分答案+模拟最后一轮喂饭的思想

通过二分答案找到刚好小于阿姨需要打饭的轮数

之后模拟最后一轮打饭的情况 对于那些还需要吃不止一轮饭的同学 按照其顺序(每一位同学在吃饭时 相对顺序是不会发生变化的)

输出即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[(ll)1e5 + 3],c[(ll)1e5+3];
ll n, k, sum = 0;
ll sumrice(ll t)
{
	ll ans = 0;
	for (int i = 1; i <= n; i++)
		ans += min(a[i],t);
	return ans;
}
 
int main()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]),
		sum += a[i];
	}
	if (k>sum) printf("-1");
	else
	{
		int l = 0, r = 1e9;
		while (l+1 < r)
		{
			ll mid = (l + r) / 2;
			if (sumrice(mid) <= k)
				l = mid;
			else
				r = mid;
		}
		int tsum = k - sumrice(l);
		int t = 0;
		for (int i = 1; i <= n; i++)
		{
			if (a[i] > l) c[++t] = i;
		}
		for (int i = tsum + 1; i <= t; i++)
			printf("%lld ",c[i]);
		for (int i = 1; i <= tsum; i++)
		{
			if (a[c[i]]!= l+1) printf("%lld ", c[i]);
		}
	}
	return 0;
}

201 任务分配

此题背景源于最小失约问题 然而看到经典的value值后 不难得出本题要用使用动态规划来做

关键在于DP数组的含义和状态转移公式

我们先考虑对于一个任务 我们能做的事有多少种?

1、做这个任务

2、不做这个任务

再考虑时间的自然增长有什么影响

因此 选择DP[i]表示在i时刻所能获得的最大金钱 确定了DP数组的含义后 重新考虑对任务的两种情况

1、做这个任务

那么在i这个时刻 我们决定要做这个任务j 就需要在start[j]-end[j]这段时间我们都没有参加别的任务 这个状态怎么模拟呢?不难想到 我们只要退回DP[start[j]]的时候 即退回start[j]时刻我们获得的最大金钱 此后从start[j]-end[j]这段时间我们获得的金钱都被删去了 可以专心做这个任务 获得这个任务的金钱

也就是第一个状态

DP[start[j]]+value[j]

2、不做这个任务

不做这个任务 那么我们获得的金钱就应该与上一时刻获得的金钱等值

DP[i]=DP[i]

然而 我们忽略了一件事

时间是公平的

即便我们此刻无所事事

也依然获得了曾经的宝藏

最终代码为

#include<bits/stdc++.h>
using namespace std;
//本题是在失约问题背景下转化的动态规划问题 我们解决五个问题
//DP数组的含义
//初始化
//状态转移方程
//遍历顺序
const int maxn = 1e3 + 5;
int n;
int st[maxn], ed[maxn], w[maxn];
int dp[maxn];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> st[i] >> ed[i] >> w[i];
	for (int i = 1; i <= 1000; i++)
	{
	  //时间的公平让我们无论如何都获得曾经获得的最大价值
		dp[i] = dp[i - 1];
		for (int j = 1; j <= n; j++)
		{
		  //而我们需要重新做任务的时候 就开始更新这个曾经获得的最大价值吧
			if (i == ed[j])
				dp[i] = max(dp[st[j]] + w[j], dp[i]);
		}
	}
	cout << dp[1000];
	return 0;
}

204 加一

看一下应该知道 本题使用动态规划去做

由于对于每个数字 经过k轮它的位数是相对固定的 那么我们可以直接先对所有数字(给定数据范围内的)进行预处理

#include<bits/stdc++.h>
using namespace std;
const long long mod = 1e9 + 7,N=200004;
int f[N][10];
int main()
{
	int t; cin >> t;
	for (int i = 0; i <= 9; i++)
	{
		f[0][i] = 1;
	}
	for (int i = 1; i <=200000; i++)
	{
		for (int j = 1; j <= 9; j++)f[i][j - 1] = f[i - 1][j];
		f[i][9] = (f[i - 1][1] + f[i - 1][0]) % mod;
	}
	for (int i = 1; i <= t; i++)
	{
		string s;
		cin >> s;
		int n;
		cin >> n;
		long long ans=0;
		for (int j = 0; j < s.size(); j++)
		{
			ans += f[n][s[j] - '0'];
			ans %= mod;
		}
		cout << ans << endl;
	}
	return 0;
}

205 跳跳

这道题很显然需要我们找到x,y(表示两点间的坐标差)的最大公约数

之后x/gcb,y/gcb压入不会重复的数据结构中就可以了 这里使用了set存

#include<bits/stdc++.h>
using namespace std;
int gcb(int x, int y)
{
	return y ? gcb(y, x % y) : x;
}
set<pair<int, int> >ans;
pair<int, int>node[502];
int n;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> node[i].first >> node[i].second;
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i != j)
			{
				int dx = node[i].first - node[j].first;
				int dy = node[i].second - node[j].second;
				int temp = gcb(dx, dy);
				ans.insert({ dx / temp,dy / temp});
			}
		}
	}
	cout << 2 * ans.size() << endl;
	return 0;
}

206 异或和或

这道题其实比较简单 在读懂题目给的条件后就可以发现

00~00

11~10 01

01~11

10~11

由以上可以看出 只要两个序列等长 且每个序列都至少有一个1 就可以进行任意的转化

找到这个规律以后就很容易AC啦

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
int n;
string a, b;
int main()
{
	cin >> n;
	while (n--)
	{
		cin >> a >> b;
		bitset<maxn>s(a);
		bitset<maxn>t(b);
		if (a.size() != b.size())
		{
			printf("NO\n");
			continue;
		}
		if (s.count() && !t.count() || t.count() && !s.count())
		{
			printf("NO\n");
			continue;
		}
		printf("YES\n");
	}
	return 0;
}

301 出栈序列判断

思路比较简单 每次压入一个数字后 就判断当前栈头元素和容器迭代器指向的元素是否相同 如果相同 那么迭代器做自增 栈做pop操作 并输出pop

这道题比较烦的一点是容易TLE 所以把所有能降低时间复杂度的都用了 比如用’\n’代替endl之类的

#include<bits/stdc++.h>
using namespace std;
int n;
stack<int>s;
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n;
	vector<int>arr(n);
	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	int cnt = 1;
	auto index = arr.begin();
	while (cnt<=n)
	{
		printf("push %d\n", cnt);
		s.push(cnt);
		cnt++;
		while (!s.empty()&&*index == s.top())
		{
			printf("pop\n");
			s.pop();
			index++;
		}
	}
	while (!s.empty())
	{
		printf("pop\n");
		s.pop();
	}
	return 0;
}

302 序列维护

感觉这种题本意可能是希望我们能够用数组加指针模拟链表一类的数据结构

然鹅C++强大的STL包罗万象

仅仅用vector的基本操作就可以完成

#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>l;
int main()
{
	cin >> n;
	while (n--)
	{
		string temp;
		cin >> temp;
		if (temp== "insert")
		{
			int x, y;
			cin >> x >> y;
			l.insert(l.begin()+x, y);
			continue;
		}
		if (temp == "delete")
		{
			int x;
			cin >> x;
			l.erase(l.begin() + x-1);
			continue;
		}
		if (temp == "query")
		{
			int x;
			cin >> x;
			cout << l[x-1] << endl;
		}
	}
	return 0;
}

303 网格判断

标准的模拟题吧…按照题意两次两层的for循环 一层遍历行一层遍历列就可以了

不过也可以在一次两层的for循环中同时处理行列

#include<bits/stdc++.h>
using namespace std;
int n,m;
char str[26][26];
int main()
{
	cin >> n;
	m = n / 2;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> str[i][j];
		}
	}
	bool check = true;
	for (int i = 1; i <= n; i++)
	{
		int hcnt=0, lcnt=0, flag=false;
		for (int j = 1; j <= n; j++)
		{
			//str[i][j]是行
			if (str[i][j] == 'B')
			{
				hcnt++;
			}
			//str[j][i]是列
			if (str[j][i]=='B')
			{
				lcnt++;
			}
			if ((str[i][j] == str[i][j - 1]&& str[i][j - 1] == str[i][j + 1] )|| str[j][i] == str[j - 1][i]&& str[j - 1][i] == str[j + 1][i])
			{
				flag = true;
				break;
			}
		}
		if (hcnt!=m||lcnt!=m||flag)
		{
			printf("0");
			check = false;
			break;
		}
	}
	if (check)
	{
		printf("1");
	}
	return 0;
}

304 整齐的数组

看到这道题 首先印象就是需要用求GCB的办法找最大公约数

我们首先明确

给定两个数 a b 若a,b可以通过若干次操作转化为同一个数 那么a b本身一定可以互相转化

因此 我的想法是不断地寻找做差后差值不为0的两个数字 保存这个差值 与下一个做差差值不为0的数的差值找最大公约数

如果最后最大公约数是1就不要再找了 直接输出

#include<bits/stdc++.h>
using namespace std;
int gcb(int x, int y)
{
	if (y>=x)
	{
		swap(x, y);
	}
	return y ? gcb(y, x % y) : x;
}
int n, t;
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> t;
	while (t--)
	{
		cin >> n;
		vector<int>v(n + 1);
		for (int i = 0; i < n; i++)
			cin >> v[i];
		int dif = fabs(v[0] - v[1]);
		int flag = true;
		for (int i = 1; i < n; i++)
		{
			int temp = fabs(v[0] - v[i]);
			if (temp == 0)
			{
				continue;
			}
			if (dif == 0)
			{
				dif = temp;
			}
			else
			{
				dif = gcb(dif, temp);
			}
			if (dif == 1)
			{
				printf("1\n");
				flag = false;
				break;
			}
		}
		if (flag)
		{
			if (dif)
				printf("%d\n", dif);
			else
				printf("-1\n");
		}
	}
	return 0;
}

305 删删

先检查本身是不是回文串

如果本身不是回文串 由于我们只能删除一个字母

所以先找到不满足回文串的两个字母

选择删除第一个和第二个

分别判断若删除第一个字符/第二个字符 能不能转化为回文串 若能 需要的操作数 然后取最小就可以了

大概是用头尾指针法了吧

#include<bits/stdc++.h>
using namespace std;
int m;
int n;
string str;
int head, tail;
bool check(int head,int tail)
{
	while (head < tail)
	{
		if (str[head] != str[tail])
			return false;
		head++;
		tail--;
	}
	return true;
}
//只在传入的head~tail区间内不符合回文串
int check(char x, int head, int tail)
{
	int cnt=0;
	while (head < tail)
	{
		if (str[head] == str[tail])
		{
			head++;
			tail--;
		}
		else
		{
			if (str[head] == x)
			{
				head++;
				cnt++;
			}
			else if (str[tail] == x)
			{
				tail--;
				cnt++;
			}
			else
				return -1;
		}
	}
	return cnt;
}
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> m;
	while (m--)
	{
		cin >> n >> str;
		head = 0, tail = str.size() - 1;
		//先判断本身是不是回文串 如果本身就是回文串就不需要了
		if (check(head, tail))
		{
			printf("0\n");
			continue;
		}
		//由于已经确定不是回文串 所以不会在找不符合回文的过程中
		//使得头指针越过尾指针 因此在这里不用考虑head<tail
		while (str[head] == str[tail])
		{
			head++;
			tail--;
		}
		char first = str[head];
		char second = str[tail];
		int fc = check(first, head, tail);
		int sc = check(second, head, tail);
		if (fc == -1 && sc == -1)
			printf("-1\n");
		else if (fc == -1 && sc != -1)
			printf("%d\n", sc);
		else if (fc != -1 && sc == -1)
			printf("%d\n", fc);
		else
			printf("%d\n", min(fc, sc));
	}
	return 0;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值