2024睿抗省赛本科组DE题解

章鱼图的判断

题意:

对于无向图\(G = (V, E)\),我们将有且只有一个环的、大于2个顶点的无向连通图称之为章鱼图,因为其形状像是一个环(身体)带着若干个树(触手),故得名。
给定一个无向图,请你判断是不是只有一个章鱼子图存在。

注意:这里的章鱼子图指的是满足章鱼图性质的极大连通子图

思路:

①有几个联通快?dfs一遍即可
②如何判断是不是“章鱼图”?对于一个合法的章鱼图,\(点数=\frac{出度数 + 入度数}{4}\)
③如何求环上的点数?dfs,保留前驱,更新深度

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 1e5 + 10;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
int head[N];
int ne[N * 2];
int idx;
int w[N * 2];
int group[N];
int cnt = 0;
int num = 0;
int dep[N];

bool flag = false;
void add (int x, int y) {
	 ne[idx] = head[x];
	 w[idx] = y;
	 head[x] = idx ++;
}
void dfs (int u, int father) {
	 group[u] = cnt;
	 for (int i = head[u]; i != -1; i = ne[i]) {
	 	 int ww = w[i];
	 	 if (ww != father && !group[ww]) {
	 	 	 dfs (ww, u);
	 	 }
	 }
}

void dfs2 (int u, int deep, int pre) {
	 dep[u] = deep;
	 for (int i = head[u]; i != -1; i = ne[i]) {
	 	 int ww = w[i];
	 	 if (ww == pre) continue;
	 	 if (ww != pre && dep[ww]) {
	 	 	 num = abs(dep[u] - dep[ww]) + 1;
	 	 	 flag = 1;
	 	 	 return ;
	 	 }
	 	 dfs2 (ww, deep + 1, u);
	 	 if (flag) return ;
	 }
}

void solve() {
	 int n, m;
	 cin >> n >> m;
	 idx = 0;
	 cnt = 0;
	 memset (head, -1, sizeof (head));
	 for (int i = 1; i <= n; ++i) {
	 	 group[i] = 0;
	 }
	 vector<int> Size (n + 1, 0);
	 vector<int> In (n + 1, 0);
	 vector<int> Out (n + 1, 0);
	 vector<int> groupIn (n + 1, 0);
	 vector<int> groupOut (n + 1, 0);
	 for (int i = 1; i <= m; ++i) {
	 	 int x, y;
	 	 cin >> x >> y;
	 	 add (x, y);
	 	 add (y, x);
	 	 In[x] ++;
	 	 In[y] ++;
	 	 Out[x] ++;
	 	 Out[y] ++;
	 }
	 for (int i = 1; i <= n; ++i) {
	 	 if (!group[i]) {
	 	 	 cnt ++;
	 	 	 dfs (i, 0);
	 	 }
	 }
	 for (int i = 1; i <= n; ++i) {
	 	 Size[group[i]] ++;
	 	 groupIn[group[i]] += In[i];
	 	 groupOut[group[i]] += Out[i];
	 }
	 int res = 0;
	 int resid = 0;
	 for (int i = 1; i <= cnt; ++i) {
	 	 if (Size[i] >= 3 && Size[i] == (groupIn[i] + groupOut[i]) / 4) {
	 	 	 res ++;
	 	 	 resid = i;
	 	 }
	 }
	 if (res >= 2 || res == 0) {
	 	 cout << "No" << " " << res << "\n";
	 }else{
	 	 flag = false;
	 	 for (int i = 1; i <= n; ++i) {
	 	 	 dep[i] = 0;
	 	 }
	 	 for (int i = 1; i <= n; ++i) {
	 	 	 if (group[i] == resid) {
	 	 	 	 dfs2 (i, 1, -1);
	 	 	     break;
	 	 	 }
	 	 }
	 	 cout << "Yes" << " " << num << "\n";
	 }
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}

工作安排

题意:

\(K\)\(N\)项工作等待完成,第\(i\)项工作需要花\(t_i\)单位时间,必须在\(d_i\)时刻或之前完成,报酬为\(p_i\)。假设小\(K\)工作时刻从\(0\)开始,且同一时刻只能做一项工作、工作一旦开始则不可中断或切换至其他工作,请你帮小\(K\)规划一下如何选择合适的工作,使小\(K\)可以获得最多的报酬。

思路:

按截止时间排序 + 倒序背包
假设你没有按截止时间排序,而是随机顺序,结果你把一个截止时间很晚的任务放进去了,占据了一段时间。然后你遇到一个截止时间早、时间短、收益高的任务,结果你却安排不进去了 → 损失最优解。
而如果你按截止时间升序处理,就能保证:每次你在当前时刻前安排能做的任务,不会冲突掉未来那些“必须早做”的任务

TLE代码

void solve() {
	 int n;
	 cin >> n;
	 for (int i = 1; i <= n; ++i) {
	 	 cin >> nd[i].x >> nd[i].y >> nd[i].z;
	 }
	 sort (nd + 1, nd + 1 + n, cmp);
	 vector<int> dp (N, 0);
	 for (int i = 1; i <= n; ++i) {
	 	 for (int j = nd[i].y; j >= 0; --j) {
	 	 	 if (j - nd[i].x >= 0) dp[j] = max (dp[j], dp[j - nd[i].x] + nd[i].z);
	 	 }
	 }
	 int res = 0;
	 for (int i = 0; i < N; ++i) {
	 	res = max (res, dp[i]);
	 }
	 cout << res << "\n";
}

问题

1、每组数据进 solve() 都会重新构造一个 vector dp(N, 0),底层走了一次堆上分配+初始化+析构。虽然 N 只有 5010,看似很小,但反复 5 次 malloc/free+5000 次写零,常数开销就被放大了。
2、把 int j、int i、int n 全抬升成 long long 以后,编译器可以直接在 R 系列(64 bit)寄存器上循环,省掉每次从 32→64 的零扩展或者 64→32 的截断资源冲刺

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;
#define int long long 
const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 5010;
//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;

struct node {
	 int x, y, z;
};

node nd[N];
int dp[N];

bool cmp (node a, node b) {
	 return a.y < b.y;
}

void solve() {
	 int n;
	 cin >> n;
	 for (int i = 1; i <= n; ++i) {
	 	 cin >> nd[i].x >> nd[i].y >> nd[i].z;
	 }
	 sort (nd + 1, nd + 1 + n, cmp);
	 memset (dp, 0, sizeof (dp));
	 for (int i = 1; i <= n; ++i) {
	 	 for (int j = nd[i].y; j >= nd[i].x; --j) {
	 	    dp[j] = max (dp[j], dp[j - nd[i].x] + nd[i].z);
	 	 }
	 }
	 int res = 0;
	 for (int i = 0; i < N; ++i) {
	 	res = max (res, dp[i]);
	 }
	 cout << res << "\n";
}

signed main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}
posted @ 2025-07-15 03:13  Li_Yujia  阅读(13)  评论(0)    收藏  举报