首页 > 其他分享 >牛客多校2024-6

牛客多校2024-6

时间:2024-08-05 18:05:41浏览次数:15  
标签:p2 suf int pn2 多校 2024 牛客 ans dp

A - Cake

(神金,害我调了一个半小时)
AliceBob 玩一个游戏。游戏分为2阶段。
阶段1:有一棵边权值为 \(0\) 或 \(1\) 的有根树,两人轮流走,Alice 先走,走到叶子就停下来。记录下经过边的权值形成一个字符串\(S\)
阶段2:Bob 将一个蛋糕切成 \(len(S)\) 块,块可以为空。然后遍历 \(S\) 的每个字符,如果为 \(0\) 就 Alice 选择一块蛋糕;如果为 \(1\) 就 Bob 选择一块蛋糕。
每个人都期望利益最大化,求 Bob 获得的蛋糕比例最大值。
有根树节点数 \(n \leq 2e5\)


首先考虑阶段2:
显然如果 \(S[0] = \ '1'\) ,Bob 能获得整块蛋糕。将这个性质推广下去,Bob 能够将蛋糕切成任意的 \(k (k \leq len(S))\) 块,从而使 \(S[k]\) 以后的字符串失去了意义。
这意味着 Bob 能够选择 \(S\) 中的一个前缀 \(S_{pre}\) 切蛋糕。然后考虑 \(k\) 块怎么切,容易发现均等切是最优的,因为在 Alice 先手的情况下更大的蛋糕块会被 Alice 拿走从而不利(Bob 先手的情况答案为1)。

然后考虑阶段1:
显然这是一个树上博弈,Alice 需要让任意 \(S_{pre}\) 中 \(1\) 的占比最大值最小,Bob 需要让 \(S_{pre}\) 中 \(1\) 的占比最大值最大。
设计dfs状态为dfs(e, cnt0, cnt1)代表进行到节点e,完成subtree(e)的计算时该玩家(可由cnt0+cnt1的奇偶得出)的最优解,于是就做完了。
此外还要注意 Alice 在最优解仍然可能走向权值为 \(1\) 得边,从而使答案变大,因此在除了根节点的每个节点都应该尝试更新答案的最大值,而不是仅在 Bob 的回合尝试更新。


const int U = 5e5;
int head[U], val[U], to[U], nxt[U], tot;
void add(int x, int y, int z)
{
	to[++tot] = y; val[tot] = z; nxt[tot] = head[x]; head[x] = tot;
}
bool vis[U];
double dfs(int e, int cnt0, int cnt1)
{
	int t = (cnt0+cnt1+1)%2;
	double ans = !t;
	vis[e] = 1;
	
	bool leaf = 1;
	for(int g = head[e]; g; g = nxt[g])
	{
		int y = to[g], z = val[g];
		if (vis[y]) continue;
		leaf = 0;

		if (t == z)
		{
			if (t == 1) ans = max(ans, dfs(y, cnt0, cnt1+1));
			else ans = min(ans, dfs(y, cnt0+1, cnt1));
		}
		else
		{
			if (t == 1) ans = max(ans, dfs(y, cnt0+1, cnt1)); // 1100100
			else ans = min(ans, dfs(y, cnt0, cnt1+1));
		}
	}

	if (leaf) ans = (double)cnt1/(cnt0+cnt1);
	else if (e-1) ans = min(ans, (double)cnt1/(cnt0+cnt1));
	return ans;
}

int T = next();
while(T--)
{
	int n = next();

	tot = 0;
	rep(i, 0, n+1) head[i] = vis[i] = 0;

	rep(i, 0, n-1)
	{
		int u, v, k;
		cin>>u>>v>>k;
		add(u, v, k);
		add(v, u, k);
	}

	cout<<fixed<<setprecision(12)<<dfs(1, 0, 0)<<endl;
}


B - Cake 2

给定正 \(n\) 边形,连接所有距离(两点之间最小边数)为 \(k\) 的顶点,求多边形内部被划分为多少区域。
\(n \in [4, 1e6], k \in [2, n-2]\)


画图可以得知:若 \(2k = n\),则答案为 \(n\);否则答案为\(n*min\{k, n-k\}+1\)。
证明直接咕了。


int n, k;
cin>>n>>k;
if (2*k == n) cout<<n<<endl;
else
{
    if (k > n/2) cout<<n*(n-k)+1<<endl;
    else cout<<n*k+1<<endl;
}


C - Cake 3

怎么是记几构造啊/jk



D - Puzzle: Wagiri

给定一张简单连同无向图,边有两种状态LunQie
现要求移除任意一些边使得图连通,同时Lun边都在环上,Qie边都不在环上,输出任意方案或无解。
\(n \leq 1e5, m \in [n-1, 2e5]\)


显然如果对一个图求e-DCC即边双连通分量,所有的边要么在一个e-DCC中要么作为桥。
因此可以忽略所有Qie边,对仅有Lun边的图求e-DCC,得出的所有桥删除后就能够满足所有Lun边都在环上的条件。
然后考虑图联通这一条件,显然需要恢复几条Qie边使各个完全不连通的e-DCC连通。于是处理出所有Qie边连接的两个e-DCC,并使用并查集逐步尝试恢复Qie边使图连通。因为并查集具有树状结构,所以保证了Qie边都不在环上。


const int U = 5e5;
int head[U], to[U], nxt[U], stat[U], tot = 1;
void add(int x, int y, int st)
{
	to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; stat[tot] = st;
}
int dfn[U], low[U], n, m, ver;
bool bridge[U];
void tarjan(int e, int ine) // 求e-DCC
{
	dfn[e] = low[e] = ++ver;
	for(int g = head[e]; g; g = nxt[g])
	{
		if (stat[g]) continue;
		int y = to[g], cc = 0;
		if (!dfn[y])
		{
			tarjan(y, g);
			low[e] = min(low[e], low[y]);

			if (dfn[e] < low[y]) bridge[g] = bridge[g^1] = 1;
		}

		else if (g != (ine^1)) low[e] = min(low[e], dfn[y]);
	}
}
int col[U], dcc;
void dfs(int e) // 编号e-DCC
{
	col[e] = dcc;
	for(int g = head[e]; g; g = nxt[g])
	{	
		int y = to[g];
		if (col[y] || stat[g] || bridge[g]) continue;
		dfs(y);
	}
}
int father[U]; // 并查集
int find(int k)
{
	if (father[k] == k) return k;
	return father[k] = find(father[k]);
}
void uni(int a, int b)
{
	father[find(a)] = find(b);
}

cin>>n>>m;
rep(i, 0, m)
{
	int u, v; string s;
	cin>>u>>v>>s;
	if (s[0] == 'L') add(u, v, 0), add(v, u, 0);
	else add(u, v, 1), add(v, u, 1);
}

hrp(i, 1, n) if (!dfn[i]) tarjan(i, 0);
hrp(i, 1, n) if (!col[i]) dcc++, dfs(i);

hrp(i, 0, dcc) father[i] = i;

set<pii> ans;
hrp(i, 1, n) for(int g = head[i]; g; g = nxt[g])
{
	int y = to[g];
	if (i > y) continue; // 由于建了双向边所以忽略一个方向的边

	if (!stat[g] && !bridge[g]) ans.insert({i, y}); // Lun边不是桥
	else if (stat[g] && find(col[i]) != find(col[y])) // Qie边作为桥
	{
		ans.insert({i, y});
		uni(col[i], col[y]);
	}
}

bool yes = 1;
hrp(i, 2, dcc) if (find(i-1) != find(i)) yes = 0;

if (!yes) cout<<"NO"<<endl;
else
{
	cout<<"YES"<<endl<<ans.size()<<endl;
	for(auto p:ans) cout<<p.first<<' '<<p.second<<endl;
}


E - Palindrome Counter

求所有长为 \(n\),字符集个数为 \(k\) 的字符串的本质不同回文子串长度之和。



F - Challenge NPC 2

(随机算法大失败)
给定一棵森林,求其补图的Hamilton路径。
\(n \leq 5e5\)

首先如果该森林为菊花,也就是补图中有一个点度数为 \(0\),显然不可能有解。
然后考虑直接对该森林进行分析。对其中一棵树从任意节点开始求深度,能够发现由于树的性质:

  1. 相同深度的节点间没有边
  2. 只有相邻深度的节点间可能有边
    于是在补图上存在性质:如果两个节点深度不相邻,那么它们之间一定有边。
    同时由于原图上树与树之间不连通,补图上两棵树的任意节点间都存在边。于是可以令:一棵树的起始节点深度为上一颗树深度最大值+1。这样可以获得编号 \(1 \textasciitilde m\) 的若干节点。由于补图性质,可以构造出2->4->...->1->3->...的顺序使所有点都连通起来。如果 \(m < 4\),则需要特殊判定。
    因为从任意的节点开始求深度并不总能获得深度的最大值,所以最终得出的总深度 \(m\) 可能小于 \(4\),从而造成错误的无解。于是应该对每棵树都从直径的一端开始求深度,从而能保证 \(m\) 取得最大,这样 \(m < 4\) 的情况只在 \(n < 4\) 时出现。

const int U = 2e6;
int tot, head[U], nxt[U], to[U];
void add(int x, int y)
{
	to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
}
int dep[U];
vector<int> vd[U];

int T;
cin>>T;
while(T--)
{
	int n, m;
	cin>>n>>m;
	hrp(i, 0, 3*n) head[i] = dep[i] = 0;
	
	rep(i, 0, m)
	{
		int u, v; cin>>u>>v;
		add(u, v); add(v, u);
	}

	if (n == 2)
	{
		if (m) cout<<-1<<endl;
		else cout<<"1 2"<<endl;
		continue;
	}
	if (n == 3)
	{
		if (m == 2) cout<<-1<<endl;
		else if (m == 1)
		{
			int t;
			hrp(i, 1, n) if (!head[i]) t = i;
			if (t == 2) cout<<"1 2 3"<<endl;
			else if (t == 1) cout<<"2 1 3"<<endl;
			else cout<<"1 3 2"<<endl;
		}
		else cout<<"1 2 3"<<endl;
		continue;
	}

	int maxd = 0;
	hrp(i, 1, n) if (!dep[i])
	{
		queue<pii> que; que.push({i, 0});
		int s, e;
		while(que.size())
		{
			auto [x, fa] = que.front(); que.pop();
			s = x;
			for(int g = head[x]; g; g = nxt[g])
			{
				int y = to[g];
				if (y != fa) que.push({y, x});
			}
		}

		que.push({s, ++maxd});
		while(que.size())
		{
			auto [x, d] = que.front(); que.pop();
			dep[x] = maxd = d;

			for(int g = head[x]; g; g = nxt[g])
			{
				int y = to[g];
				if (!dep[y]) que.push({y, d+1});
			}
		}
	}

	hrp(i, 0, maxd) vd[i].clear();
	hrp(i, 1, n) vd[dep[i]].pb(i);

	if (maxd < 4) cout<<-1<<endl;
	else
	{
		for(int d = 2; d <= maxd; d += 2) for(auto i:vd[d]) cout<<i<<' ';
		for(int d = 1; d <= maxd; d += 2) for(auto i:vd[d]) cout<<i<<' ';
		cout<<endl;
	}
}


G - Easy Brackets Problem

/?



H - Genshin Impact's Fault

模拟Genshin抽卡,卡分为3星,4星,A5星,B5星4种。每10抽不能只有3星,每90抽至少有1个5星,抽到的5星必须AB交替。


int T = next();
while(T--)
{
	string str = next<string>();

	bool no = 0;
	if (str.size() >= 10) hrp(i, 0, str.size()-10)
	{
		bool all3 = 1;
		rep(j, i, i+10) if (str[j] != '3') all3 = 0;
		if (all3)
		{
			no = 1;
			break;
		}
	}

	if (str.size() >= 90) hrp(i, 0, str.size()-90)
	{
		bool no5 = 1;
		rep(j, i, i+90) if (str[j] == '5' || str[j] == 'U') no5 = 0;
		if (no5)
		{
			no = 1;
			break;
		}
	}

	string s5;
	rep(i, 0, str.size()) if (str[i] == '5' || str[i] == 'U') s5 += str[i];
	if (s5.size()) rep(i, 0, s5.size()-1) if (s5[i] != 'U' && s5[i+1] != 'U') no = 1;

	if (no) cout<<"invalid"<<endl;
	else cout<<"valid"<<endl;
}


I - Intersecting Intervals

给定一个数字矩阵,每一行选择一个区间,相邻行区间必须有交,求选中数字和的最大值。
\(n*m \leq 1e6\)

一眼dp,很容易想出状态dp(i, j)代表dp在第i行选了j后的最大值
有一个显然的转移枚举该行 \(k\),强制该行选 \(j \textasciitilde k\),但 \(j\) 前和 \(k\) 后的数字还可能被选使答案更大。因此用O(m)复杂度预处理出每行的pre(k), suf(k),分别代表包含 \(k\) 选 \(k\) 前和 \(k\) 后一些序列的最大贡献。
于是有:
dp(i, k) = dp(i-1, j)+sum(j...k)+pre(j)+suf(k)(j<k) dp(i, k) = dp(i-1, j)+sum(k...j)+pre(k)+suf(j)(j>k) 从而得出了O(mn^2)`的转移(其中`sum, pre, suf数组均右移了1位`):

rep(j, 0, m) hrp(k, 0, j) dp[i][j] = max(dp[i][j], dp[i-1][k]+sum[j+1]-sum[k]+pre[k]+suf[j+2]);
rep(j, 0, m) rev(k, m-1, j) dp[i][j] = max(dp[i][j], dp[i-1][k]+sum[k+1]-sum[j]+pre[j]+suf[k+2]);

复杂度不够,于是需要优化,有两种办法(仅说明 \(j < k\) 情况):

  1. 关注定值:能够发现sum[j+1]+suf[j+2]为定值,于是提出定值并在转移 \(j\) 的时候保存已经遍历过的dp[i-1][k]+sum[k]+pre[k]的最大值。
  2. 关注转移 \(j\) 过程:设V=dp[i-1][p]+sum[p+1]-sum[k]+pre[k]+suf[p+2],\(j = 1 \textasciitilde p\) 时包含所有 \(V\) 取值的集合为 \(S_p\) 。能够发现V'=dp[i-1][p]+sum[p+2]-sum[k]+pre[k]+suf[p+3]=V+sum[p+1]-sum[p]+suf[p+3]-suf[p+2]=V+w[p+1]+suf[p+3]-suf[p+2],从而让所有 \(V'\) 和dp[i-1][p+1]+v[i][p+1]+pre[p+1]+suf[p+3]一起构成了\(S_{p+1}\)。

代码采用的是优化2:

int T = next();
while(T--)
{
	v.clear();
	dp.clear();

	int n, m;
	cin>>n>>m;
	rep(i, 0, n+5) v.pb(vector<int>()), dp.pb(vector<int>());

	hrp(i, 1, n) rep(j, 0, m) v[i].pb(next());
	rep(i, 0, n+5) rep(j, 0, m+5) dp[i].pb(i ? -INF : 0);

	hrp(i, 1, n)
	{
		vector<int> sum{0}, pre{0}, suf{0};

		rep(j, 0, m) sum.pb(sum.back()+v[i][j]);
		rep(j, 0, m) pre.pb(max(pre.back()+v[i][j], 0LL));
		rev(j, m-1, 0) suf.pb(max(suf.back()+v[i][j], 0LL));
		reverse(suf.begin(), suf.end());
		suf.insert(suf.begin(), 0);
		pre.insert(pre.end(), 0);

		int maxx = -INF;
		rep(j, 0, m)
		{
			maxx += v[i][j]-suf[j+1]+suf[j+2];
			maxx = max(maxx, dp[i-1][j]+v[i][j]+pre[j]+suf[j+2]);
			dp[i][j] = maxx;
		}
		maxx = -INF;
		rev(j, m-1, 0)
		{
			maxx += v[i][j]-pre[j+1]+pre[j];
			maxx = max(maxx, dp[i-1][j]+v[i][j]+pre[j]+suf[j+2]);
			dp[i][j] = max(dp[i][j], maxx);
		}
	}

	int ans = -INF;
	rep(j, 0, m) ans = max(ans, dp[n][j]);
	cout<<ans<<endl;
}

优化1:

int maxx = -INF;
rep(j, 0, m)
{
	maxx = max(maxx, -sum[j]+pre[j]+dp[i-1][j]);
	dp[i][j] = max(dp[i][j], maxx+sum[j+1]+suf[j+2]);
}

maxx = -INF;
rev(j, m-1, 0)
{
	maxx = max(maxx, sum[j+1]+suf[j+2]+dp[i-1][j]);
	dp[i][j] = max(dp[i][j], maxx-sum[j]+pre[j]);
}


J-Stone Merging

有 \(n\) 堆石子,开始时每堆 \(1\) 个,每个石子被可重复编号。有 \(k-1\) 个机器编号为 \(2 \textasciitilde k\)。
编号为 \(g\) 的机器可以合并 \(g\) 堆石子;但如果这 \(g\) 堆石子中有至少 \(1\) 个编号为 \(g\) 的石子,该编号为 \(g\) 的机器会不能再使用,并将所有编号为 \(g\) 的石子提取出并放成一堆,把剩下的石子放成另一堆。
输出将所有石子合成一堆的方案或无解。
\(1 \leq k \leq n \leq 1e5\)

除了仅有 \(2\) 堆编号为 \(2\) 的石子的情况,\(2 \textasciitilde k\)的编号都在石子上出现过是无解的。这种情况先进行特判。
首先编号为 \(2\) 的机器人一定存在,而且功能十分强大,能够将所有非 \(2\) 石子都合成一堆。
讨论:

  1. 只存在编号为 \(2\) 的机器人。由于特判过,所以不可能存在编号为\(2\)的石子。全都合并即可。
  2. 存在编号为 \(t\) 的机器人,使得编号为 \(t\) 的石头不存在。所以当前的任务为把石子堆数从 \(n\) 减少到 \(t\)(官方题解采用方法不同)。因为 \(3\) 号机器人存在,所以我们可以用 \(3\) 号机器人把编号为 \(2\) 的石子堆成双减少;同时用 \(2\) 号机器人把编号不为 \(2\) 的石子堆逐个减少。此外还可以在最后使用 \(2\) 号机器人把编号为 \(2\) 的石子合并为 \(1\) 堆。如果石子堆个数无法达到 \(t\),则无解(事实上一定有解)。
    使用set实现而非vector来降低删除操作的时间复杂度。

官方题解的方法为第一步将\(y=(n−1)~mod~(x−1)+1\) 个石子合成成一堆, 后面每次都使用机器 \(t\) 来合成。


const int U = 1e6;
int w[U];
bool exist[U];

int T = next();
while(T--)
{
	int n, k, op = 0;
	cin>>n>>k;
	hrp(i, 1, n) exist[i] = 0;

	set<int> p2, pn2;
	vector<vector<int>> ans;

	hrp(i, 1, n)
	{
		cin>>w[i];
		if (w[i] == 2) p2.insert(i);
		else pn2.insert(i);
		exist[w[i]] = 1;
	}

	if (!pn2.size() && p2.size() == 2)
	{
		cout<<"1"<<endl<<"2 1 2"<<endl;
		continue;
	}

	int tar = 0;
	rev(i, k, 2) if (!exist[i])
	{
		tar = i;
		break;
	}

	if (!tar)
	{
		cout<<-1<<endl;
		continue;
	}

	if (k == 2) while(pn2.size() > 1)
	{
		vector<int> v{*pn2.begin(), *next(pn2.begin())};
		ans.pb(v);
		pn2.erase(pn2.begin());
		pn2.erase(pn2.begin());
		op++;
		pn2.insert(n+2*op);
	}
	else
	{
		bool mat = 0;
		if (p2.size()+pn2.size() == tar) mat = 1;
		
		while(pn2.size()+p2.size() > tar+1 && p2.size() > 2 && !mat)
		{
			vector<int> v{*p2.begin(), *next(p2.begin()), *next(next(p2.begin()))};
			ans.pb(v);
			p2.erase(p2.begin());
			p2.erase(p2.begin());
			p2.erase(p2.begin());
			op++;
			p2.insert(n+2*op);

			if (p2.size()+pn2.size() == tar) mat = 1;
		}
		
		while(pn2.size() > 1 && !mat)
		{
			vector<int> v{*pn2.begin(), *next(pn2.begin())};
			ans.pb(v);
			pn2.erase(pn2.begin());
			pn2.erase(pn2.begin());
			op++;
			pn2.insert(n+2*op);

			if (p2.size()+pn2.size() == tar) mat = 1;
		}

		if (p2.size()+pn2.size() == tar+1 && p2.size() > 1)
		{
			vector<int> v{*p2.begin(), *next(p2.begin())};
			ans.pb(v);
			p2.erase(p2.begin());
			p2.erase(p2.begin());
			op++;
			p2.insert(n+2*op-1);
			mat = 1;
		}

		if (mat)
		{
			vector<int> v;
			for(auto i:p2) v.pb(i);
			for(auto i:pn2) v.pb(i);
			ans.pb(v);
		}
		// else
		// {
		//	cout<<-1<<endl;
		//	continue;
		// }
	}

	cout<<ans.size()<<endl;
	for(auto i:ans)
	{
		cout<<i.size()<<' ';
		for(auto j:i) cout<<j<<' ';
		cout<<endl;
	}
}


K - The Great Wall 2

给定长为 \(n\) 的整数序列,要求分为恰好 \(k\) 个非空连续段使得这 \(k\) 段的极差之和最小。
要求对 \(k = 1 \textasciitilde n\) 分别求解
\(n \leq 5000\)

不会,zzz

标签:p2,suf,int,pn2,多校,2024,牛客,ans,dp
From: https://www.cnblogs.com/Loxilante/p/18343784/nowcoder24-6

相关文章

  • 学习笔记 韩顺平 零基础30天学会Java(2024.8.5)
    P460八大Wrapper类     黄色的父类是number,黑色的是自己独立的P461装箱和拆箱     手动装箱示例:                             intn1=100;                Intergerinterger=newInterger(n1);//......
  • 最新!2024年—华为认证HCIA考试报名攻略分享
    HCIAHCIA是华为初级认证。HCIA认证定位于中小型网络的设计、实施和维护,也是三种级别认证中最初级的认证。HCIA方向HCIA认证条件无HCIA认证考试考试代码:H12-811考试类型:笔试(一科)试卷题型:单选题、多选题、判断题、填空题、拖拽题考试时长:90min及格分/总分:......
  • 2024.8.5 test
    A你可以花费\(x^2\)的代价使\(A_i\)加上\(x\),\(x\ge0\),最后再加上代价为\(c\sum|A_i-A_{i-1}|\),问最小代价。\(n\le10^5\)。我们可以把序列分成若干“山峰”以及“山谷”,山峰是不会加的。考虑从山谷开始做,即每次取出最小值。设一开始处理\(A_i\),发现\(A_i\)最多是......
  • 云原生周刊:Knative 1.15 版本发布|2024.8.5
    开源项目推荐helm-secretshelm-secrets是一个Helm插件,用于动态解密加密的Helm值文件。TofuControllerTofuController(以前称为WeaveTF-Controller)是Flux的一个控制器,用于以GitOps方式协调OpenTofu和Terraform资源。TracetestTracetest是一个使用OpenTelem......
  • 2024牛客暑期补题 4 I Friends
    新手做题当然会有许多的经验。本人就是蒟蒻(这个题用到map作为预备大二)还没有完全学懂stl但是大体内容学的差不多。用到图论的知识以及set的自动排序和去重以及双指针就可以做。大家要是像我一样水平可以先去看看这几个知识图论看怎么构建set了解一下就行双指针最好去......
  • 【数据分享】2024最新安徽省镇级行政区划矢量shp
    今天要分享的数据是2024最新安徽省镇级行政区划矢量shp。   数据介绍安徽建省公元1667年,省名取当时安庆、徽州两府首字合成,因境内有皖山、春秋时期有古皖国而简称皖。它位于中国中东部,是最具活力的长江三角洲组成部分。全省南北长约570公里,东西宽约450公里。总面积14.01万......
  • 【2024-08-02】满满的爱
    20:00一个人的泳池,可以游得很快!一群人的泳池,可以游得更快!我不是一个人在战斗,背后是强大的中国队!大家的期望,我在下水前的一刻感受到了。我的这块金牌,献给伟大的祖国!                                   ......
  • 【2024-08-03】连岳摘抄
    23:59遇到困难时爆发的力量让我更坚定地面对后面的比赛。                                                 ——樊振东中国的城市化已经到了后程。后程的认知与前程......
  • 2024最全 Java 面试八股文(程序员必备)
     一、Java基础篇1.接口和抽象类的区别相似点:(1)接口和抽象类都不能被实例化(2)实现接口或继承抽象类的普通子类都必须实现这些抽象方法不同点:(1)抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,(2)抽象类可以有构造方法,而接口没有(3)抽象类中的成员变......
  • 免费【2024】springboot 大学校园旧物捐赠网站的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......