2024年大学生计算机大赛决赛-个人赛参考代码

比赛链接

A.退休

代码

void solve()
{
  int a, b;
  cin >> a >> b;
  
  int num = a + b;
  int res = (1000000 + num - 1) / num;
  cout << res / 12 << ' ' << res % 12;
}

B.四季

代码

void solve()
{
	int a, b;
	scanf("%d-%d", &a, &b);
	if (b >= 3 && b <= 5) cout << "spring";
	else if (b >= 6 && b <= 8) cout << "summer";
	else if (b >= 9 && b <= 11) cout << "autumn";
	else cout << "winter";
}

C.乘法运算

代码

void solve()
{
  long long a, b, c;
  cin >> a >> c;
  
  b = c - a;
  cout << b << '\n' << a * b;
}

D.三数最大

代码

char a, b, c;

void solve()
{
  cin >> a >> b >> c;
  
  char res = 'A';
  if (a == '>') {
  	if (b == '<') res = 'C';
  	
  	if (c == '>') return void(cout << "-1");
  }
  else {
  	res = 'B';
  	if (c == '<') res = 'C';
  	if (b == '>') return void(cout << "-1");
  }
  
  cout << res;
}

E.小T的误判

代码

string s;

void solve()
{
  cin >> s;
  
  int res = 0;
  for (int i = 0; i < si(s); i += 2)
  {
  	if (s[i] == 'T') i ++;
  	if (s[i] == 'W' && i + 2 < si(s) && s[i + 2] == 'C') res ++; 
  }
  
  cout << res;
}

F.村庄的友谊

思维

思路

  • 一个质数只能是通过1乘以它本身得到,所以我们只需枚举出序列中1的个数和质数的个数,再进行搭配。而非质数的个数就是所有两两搭配的方案数减去质数的方案数。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

ll n;
int a[N];

bool is_prime(int x)
{
	if (x < 2) return false;
	for (int i = 2; i <= x / i; i ++)
		if (x % i == 0) return false;
	return true;
}

void solve()
{
  cin >> n;
  ll cnt = 0, sum = 0;
  for (int i = 1; i <= n; i ++) 
  {
  	int x;
  	cin >> x;
  	if (x == 1) cnt ++;
  	else if (is_prime(x)) sum ++;
  }
  sum *= cnt;
  
  ll res = n * (n - 1) / 2;
  cout << res - sum;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

G.再多一些0

思维、数学

思路

  • 要使相乘的数中后缀0个数最多,即是要10的因子个数最多,而10由25得到,即是要25的个数最多。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n, m, res;
string s;
string sal;

int count_2(int x)
{
	int res = 0;
	while (x % 2 == 0) res ++, x /= 2;
	return res;
}

int count_5(int x)
{
	int res = 0;
	while (x % 5 == 0) res ++, x /= 5;
	return res;
}

void solve()
{
  cin >> n >> m;
  
  int cnt2 = count_2(n);
  int cnt5 = count_5(n);
  
  ll num = 1;
  for (int i = 0; i < cnt5; i ++)
  {
  	if (num * 2 > m) break;
  	num *= 2;
  }
  for (int i = 0; i < cnt2; i ++)
  {
  	if (num * 5 > m) break;
  	num *= 5;
  }
  
  // out(num); ent;
  
  while (num * 10 <= m) num *= 10;
  cout << m / num * num;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

H.图样图森破

推公式、数学、枚举、二分

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n, L;
int x;

bool check(int mid)
{
	ll res = L;
	ll cnt = 1LL << mid;
	ll sum = (cnt - 1) * 150;
	return res * cnt - sum >= x;
}

void solve()
{
  cin >> L >> n;
  
  while (n --)
  {
  	cin >> x;
  	if (x == L) {
  		cout << 0;
  		ent;
  		continue;
  	}
  	
  	int l = 1, r = 63;
  	while (l < r)
  	{
  		int mid = (l + r) >> 1;
  		if (check(mid)) r = mid;
  		else l = mid + 1;
  	}
  	if (check(l)) {
  		ll cnt = 1LL << l;
  		ll sum = L * cnt - 150 * (cnt - 1);
  		if (sum == x) cout << l << '\n';
  		else cout << -1 << '\n';
  	}
  	else cout << -1 << '\n';
  }
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

I.开光灯

前置知识:差分、前缀和

思路

  • 操作多个区间,最终要得到全部点或某个点的操作数。这是经典的差分、前缀和思路。

  • 因为灯只有开关两种状态且开始默认为关,所以进行奇数次操作后灯最终状态为开,偶数次操作后灯最终状态为关。可以将每个灯的操作数除余 \(2\),以 \(0\) 表示灯关、以 \(1\) 表示灯开,然后做一个前缀和预处理出 \([1, i](1 \le i \le n)\) 内有多少个灯是开的。

  • 最终思考如果第 \(i\) 个操作不做对整体的影响。可以容易发现,对于第 \(i\) 个操作的区间外的灯没有影响,只有区间内的灯会发生改变。如何改变呢?很明显是区间内的每个灯的操作数减一。如果该灯原本最终被操作奇数次,那么不做这次操作就变为偶数次,也即是原本最终是关的灯,会变为开,原本最终是开的灯,会变为关。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n, m;
int a[N];
PII b[N];

void solve()
{
  cin >> n >> m;
  for (int i = 0; i < m; i ++)
  {
  	int l, r;
  	cin >> l >> r;
  	a[l] += 1, a[r + 1] -= 1;
  	b[i] = {l, r};
  }
  
  for (int i = 1; i <= n; i ++) a[i] += a[i - 1];
  for (int i = 1; i <= n; i ++)
  {
  	
  	a[i] %= 2;
  	a[i] += a[i - 1];
  }
  
  for (int i = 0; i < m; i ++)
  {
  	int l = b[i].fi, r = b[i].se;
  	int res = a[l - 1] + a[n] - a[r] + (r - l + 1 - a[r] + a[l - 1]);
  	cout << res << ' ';
  }
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

J.上班规划

前置知识:图论、最短路、分层图

思路

  • 去掉骑车的选项就是简单的最短路模板题。

  • 骑车可以缩短到达公司的时间,但有限制条件是必须在一个停车点停车再走路到公司,所以最后一定是走路到公司。所以,想到:从公司出发到所有停车点的步行最短时间,做一遍dijkstra。再思考从起点出发到所有停车点的最短时间,此时与从公司出发不同,从起点出发遇见上车点就上车,贪最短时间,做一遍dijkstra。最终枚举所有停车点,将求出的两个最短时间求和,再取最小的那个作为答案。费用则是在第二个dijkstra时同步维护即可。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

ll n, m, num, wt, bt, v;
bool vis[N], st[N], stt[N];
vector<int> g[N];
int s, e;
ll d[N], dd[N], w[N];

void dijkstra(int u, ll c[], bool flag)
{
	for (int i = 1; i <= n; i ++) c[i] = inf, st[i] = 0;
	priority_queue<PLL, vector<PLL>, greater<PLL>> pq;
	c[u] = 0;
	pq.push({c[u], u});
	
	while (!pq.empty())
	{
		auto t = pq.top();
		pq.pop();
		
		if (st[t.se]) continue;
		st[t.se] = 1;
		for (int i : g[t.se])
		{
			ll wi = wt;
			if (flag && (vis[t.se] || stt[t.se])) wi = bt, stt[i] = 1;
			if (c[i] >= t.fi + wi) {
				if (wi == bt && (w[i] > w[t.se] + v || !w[i])) w[i] = w[t.se] + v;
				c[i] = t.fi + wi;
				pq.push({c[i], i});
			}
		}
	}
}

void solve()
{
  cin >> n >> m >> num >> wt >> bt >> v;
  for (int i = 0; i < m; i ++)
  {
  	int x;
  	cin >> x;
  	
  	vis[x] = 1;
  }
  
  while (num --)
  {
  	int a, b;
  	cin >> a >> b;
  	g[a].pk(b);
  	g[b].pk(a);
  }
  
  cin >> s >> e;
  
  dijkstra(s, d, 1);
  dijkstra(e, dd, 0);
  
  ll res = dd[s], ans = 0;
  for (int i = 1; i <= n; i ++)
  	if (vis[i]) {
  		ll sum = d[i] + dd[i];
  		if (sum < res) res = sum, ans = w[i];
  		else if (sum == res && ans > w[i]) ans = w[i];
  	}
  
  cout << res << ' ' << ans;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

K.数组涂染

前置知识:图论、最小生成树

思路

  • 首先位于一号格子时,先进行染红操作。

  • 由一个红点到白点的权值是 \(lcm(a_i, a_j)\),而红点之间权值为0,且染色操作是任意选择任意次数。所以,问题变为:每个白点选择一个与其权值最小的红点后加入红点集合,或者是从起点出发可以到达所有点的最小花费。由此容易想到这是最小生成树。想到后照着最小生成树的模板填空就行。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n;
int a[N], p[N];

struct node {
	int w, u, v;
};

vector<node> g;

int lcm(int a, int b)
{
	return a / __gcd(a, b) * b;
}

int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

ll kruskal()
{
	for (int i = 1; i <= n; i ++) p[i] = i;
	
	ll res = 0;
	for (auto [w, u, v] : g)
	{
		int pu = find(u), pv = find(v);
		if (pu != pv) 
		{
			p[pu] = pv;
			res += w;
		}
	}
	return res;
}

void solve()
{
  cin >> n;
  ll res = 0;
  for (int i = 1; i <= n; i ++) cin >> a[i], res += a[i];
  
  for (int i = 1; i < n; i ++)
  	for (int j = i + 1; j <= n; j ++)
  		g.pk({lcm(a[i], a[j]), i, j});
  		
  sort(all(g), [&](node a, node b){
  	return a.w < b.w;
  });
  
  res += kruskal();
  
  cout << res;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

L.小可的red子序列

思维、前缀和

思路

  • 求所有子串中的red子序列的和,可以转换思路,求所有red子序列属于多少个子串。

  • 对于任意一个red子序列,最短的子串即是左端点为r、右端点为d的子串,如果继续延申,即是r延申到第一个字符、d延申到最后一个字符,进行搭配组合。所以对于red子序列中,左端点r会提供它的前缀长度的贡献、右端点d会提供它的后缀长度的贡献,例如:eerdedee,r会提供前缀“eer”长度为3的贡献、d会提供后缀“dee”长度为3的贡献,最终总贡献为3*3=9个。

  • 由此,我们可以对每个r的贡献、d的贡献做前缀和,枚举每个e时就可以算出其所在的所有red子序列所属的子串个数。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n;
string s;
ll r[N], d[N];

void solve()
{
  cin >> s;
  s = ' ' + s;
  
  for (int i = 1; i < si(s); i ++)
  	r[i] = (r[i - 1] + (s[i] == 'r' ? i : 0)) % P;
  for (int i = si(s) - 1; i; i --)
  	d[i] = (d[i + 1] + (s[i] == 'd' ? si(s) - i : 0)) % P;
  
  ll res = 0;
  for(int i = 1; i < si(s); i ++)
  	if (s[i] == 'e') res = (res + r[i] * d[i]) % P;
  	
  cout << res;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

M.寻找jyuwin

二维差分前缀和、双指针

思路

  • 首先,判断一个矩阵内是否包含“jyuwin”的字符串可以使用二维前缀和,即先处理出“jyuwin”每个字符的二维网格上的个数,随后枚举矩阵,如果矩阵内包含“jyuwin”每个字符个数至少一个,就是符合要求的矩阵,将所有符合的矩阵取一个面积最小值。

  • 重点是如何枚举矩阵,因为整个二维网格大小是 \(n \times m \le 10^5\),枚举矩阵是枚举左上端点和右下端点就是四重循环,时间复杂度 \(O(n^2 \times m^2)\),会超时。

  • 想到用双指针进行优化。通过枚举两个端点的x轴坐标,用双子针求最小面积下的y轴坐标,时间复杂度变为 \(O(n^2 \times m)\)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 1e6 + 10;
const int M = 1000 + 10;

int n, m;

void solve()
{
  cin >> n >> m;
  vector<vector<char>> g(n + 1, vector<char>(m + 1));
  for (int i = 1; i <= n; i ++)
  	for (int j = 1; j <= m; j ++)
  		cin >> g[i][j];
  	
  vector<vector<int>> sj(n + 1, vector<int>(m + 1)), sy(n + 1, vector<int>(m + 1)), su(n + 1, vector<int>(m + 1)), sw(n + 1, vector<int>(m + 1)), sI(n + 1, vector<int>(m + 1)), sn(n + 1, vector<int>(m + 1));
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++)
		{
			sj[i][j] = sj[i - 1][j] + sj[i][j - 1] - sj[i - 1][j - 1] + (g[i][j] == 'j');
			sy[i][j] = sy[i - 1][j] + sy[i][j - 1] - sy[i - 1][j - 1] + (g[i][j] == 'y');
			su[i][j] = su[i - 1][j] + su[i][j - 1] - su[i - 1][j - 1] + (g[i][j] == 'u');
			sw[i][j] = sw[i - 1][j] + sw[i][j - 1] - sw[i - 1][j - 1] + (g[i][j] == 'w');
			sI[i][j] = sI[i - 1][j] + sI[i][j - 1] - sI[i - 1][j - 1] + (g[i][j] == 'i');
			sn[i][j] = sn[i - 1][j] + sn[i][j - 1] - sn[i - 1][j - 1] + (g[i][j] == 'n');
		}
	
	int res = INF;
	
	if (!sj[n][m] || !sy[n][m] || !su[n][m] || !sw[n][m] || !sI[n][m] || !sn[n][m]) return void(cout << -1);
	
	int minp = min(n, m), maxp = max(n, m);
	
	for (int i = 1; i <= minp; i ++)
	{
		for (int ii = 1; ii <= i; ii ++)
		{
			for (int j = 1, jj = 1; j <= maxp; j ++)
			{
				while (jj <= j) {
					int ix = i, iix = ii, jy = j, jjy = jj;
					if (m < n) swap(ix, jy), swap(iix, jjy);
					int cntj = sj[ix][jy] - sj[iix - 1][jy] - sj[ix][jjy - 1] + sj[iix - 1][jjy - 1];
					int cnty = sy[ix][jy] - sy[iix - 1][jy] - sy[ix][jjy - 1] + sy[iix - 1][jjy - 1];
					int cntu = su[ix][jy] - su[iix - 1][jy] - su[ix][jjy - 1] + su[iix - 1][jjy - 1];
					int cntw = sw[ix][jy] - sw[iix - 1][jy] - sw[ix][jjy - 1] + sw[iix - 1][jjy - 1];
					int cnti = sI[ix][jy] - sI[iix - 1][jy] - sI[ix][jjy - 1] + sI[iix - 1][jjy - 1];
					int cntn = sn[ix][jy] - sn[iix - 1][jy] - sn[ix][jjy - 1] + sn[iix - 1][jjy - 1];
					
					if (cntj && cnty && cntu && cntw && cnti && cntn) {
						res = min(res, (jy - jjy + 1) * (ix - iix + 1));
						jj ++;
					}
					else break;
				}
			}
		}
	}
	
	cout << res;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}

N.石头剪刀布大赛

树形dp

代码

#include <iostream>
#include <vector>
using namespace std;

const int N = 1e6 + 10;

int n;
int a[N];
int dp[N][5];
vector<int> g[N];
int all[5], cnt[5];

void dfs1(int u, int fa)
{
    all[a[u]]++;
    for (int x : g[u])
    {
        if (x == fa)
            continue;
        dfs1(x, u);
    }
}
void dfs2(int u, int fa)
{
    if (cnt[a[u]] != 0)
    {
        if (a[u] == 1)
            dp[u][2]++;
        if (a[u] == 2)
            dp[u][3]++;
        if (a[u] == 3)
            dp[u][1]++;
    }
    cnt[a[u]]++;
    int tem = cnt[a[u]];
    for (int x : g[u])
    {
        if (x == fa)
            continue;
        dfs2(x, u);
        if (cnt[a[u]] != tem)
        {
            if (a[u] == 1)
                dp[1][2]++, dp[x][2]--;
            if (a[u] == 2)
                dp[1][3]++, dp[x][3]--;
            if (a[u] == 3)
                dp[1][1]++, dp[x][1]--;
        }
    }
    if (cnt[a[u]] != all[a[u]])
    {
        if (a[u] == 1)
            dp[u][2]++;
        if (a[u] == 2)
            dp[u][3]++;
        if (a[u] == 3)
            dp[u][1]++;
    }
}
void dfs3(int u, int fa)
{
    for (int x : g[u])
    {
        if (x == fa)
            continue;
        dp[x][1] += dp[u][1];
        dp[x][2] += dp[u][2];
        dp[x][3] += dp[u][3];
        dfs3(x, u);
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 2; i <= n; i++)
    {
        int l, r;
        cin >> l >> r;
        g[l].push_back(r);
        g[r].push_back(l);
    }
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    dfs1(1, -1);
    dfs2(1, -1);
    dfs3(1, -1);
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if (dp[i][a[i]] == 0)
            ans++;
    }
    cout << ans << '\n';

    return 0;
}

O.搞大新闻

前置知识:01背包问题、贪心

思路

  • 首先,这道题就是一道01背包的变形题,多一个限制条件时间区间,而关于活动安排选择问题则是一道经典的贪心问题:解决方法是按活动结束时间进行排序后,根据所需考虑当前活动的上一个活动的状态,记录答案。

  • 所以,将贪心和01背包结合。先将所有新闻按结束时间排序,记录每个新闻的上一个时间不相交的新闻是哪个,然后套用01背包的模板就完成了此题。

    • 定义 \(dp[i][j]\) 为完成第 \(i\) 个新闻消耗 \(j\) 点精力值的状态下获得的最大收益。
    • 当精力足够时,每个新闻由其确定的上一个新闻转移而来,设 \(pre[i]\) 为第 \(i\) 个新闻的上一个新闻,则状态转移为:\(dp[i][j] = max(dp[i][j], dp[pre[i]][j - w[i]] + v[i])\)
    • 当精力不足时,第 \(i\) 新闻没法完成,此时自然是存储上一个结束新闻的最优解,状态转移为:\(dp[i][j] = dp[i - 1][j]\)。注意这里讲的上一个结束的新闻是排序排在当前新闻前面的那个新闻,而前面讲的上一个新闻\(pre[i]\),是结束时间早于当前新闻开始时间的新闻。
  • 因为每个新闻的上一个时间不相交的新闻是固定的,所以01背包不能优化为一维写法。

  • 时间复杂度:\(O(n^2)\)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <iomanip>
#include <numeric>
#include <unordered_map>
#include <unordered_set>

#define endl        '\n'
#define ll          long long
#define PII         pair<int, int> 
#define PLL         pair<ll, ll>
#define all(a)      a.begin(), a.end()
#define lowbit(x)   x & -x
#define ent         cout << '\n'
#define out(x)      cout << x << ' '
#define out2(x, y)  cout << x << " ~ " << y << ' '
#define me(a, b)    memset(a, b, sizeof a)
#define mc(a, b)    memcpy(a, b, sizeof a)
#define pk          push_back
#define ur(x)       sort(all(x)), x.erase(unique(all(x)), x.end())
#define fi          first
#define se          second
#define si(x)       int(x.size())
#define chi(x)      (x - '0')
#define ull         unsigned long long
#define Mp          make_pair

using namespace std;

const ll inf = 1e18;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
const int mod = 998244353;
const int P = 1e9 + 7;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int N = 5000 + 10;
const int M = 1000 + 10;

int n, m, t;
struct node {
	int b, e, v, w;
} g[N];
int pre[N];
int dp[N][M];

void solve()
{
  cin >> n >> m >> t;
  for (int i = 1; i <= n; i ++)
  	cin >> g[i].b >> g[i].e >> g[i].v >> g[i].w;
  	
  sort(g + 1, g + 1 + n, [&](node a, node b){
  	return a.e < b.e;
  });
  
  for (int i = 1; i <= n; i ++)
  	for (int j = 1; j < i; j ++)
  		if (g[j].e < g[i].b) pre[i] = j;
  		
  for (int i = 1; i <= n; i ++)
  	for (int j = 1; j <= m; j ++)
  	{
  		dp[i][j] = dp[i - 1][j];
  		if (g[i].v > j) continue;
  		dp[i][j] = max(dp[i][j], dp[pre[i]][j - g[i].v] + g[i].w);
  	}
  	
  cout << dp[n][m];
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

  int t = 1;
  // cin >> t;

  while (t --) solve();

  return 0;
}
posted @ 2024-11-17 15:57  Natural_TLP  阅读(272)  评论(0)    收藏  举报