模拟赛三补题报告

李智航

S13494

题目报告:

比赛中第一AC分,第二题40分,第三题10分,第四步40分;比赛后全部AC

赛中概况:

第一题吓一跳,动态规划里出了一道struct的题,非常简单 ,第二题没有判断特殊值,直接打了一个枚举,超时。第三题思路正确了,就是一个哈夫曼树的求哈夫曼值,但是忘记考虑了除不尽数位需要补0的情况。第四题直接就蒙了,看了看样例,推了一下,得了40分。

解题报告

1.IP地址(ip)

1.题目:题目描述

在这里插入图片描述
在这里插入图片描述

2.开始代码:

#include<iostream>
#include <stdio.h>
using namespace std;
const int N=1e6+10;
struct S{
	string s;
	string s1;
}a[N];
#define itn int
#define endl "\n"
int main(){
	freopen("ip.in","r",stdin);
	freopen("ip.out","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].s>>a[i].s1;
	}
	int T;
	scanf("%d",&T);
	string s2;
	for(int i=1;i<=T;i++){
		bool f=true;
		cin>>s2;
		for(int j=1;j<=n;j++){
			if(s2==a[j].s1&&f){
				cout<<a[j].s<<endl;
				f=false;
			}
		}
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

思路:纯暴力,因为string能直接比较,所以直接用struct存两个信息,然后比较第二个信息就行,
比较出来了后找到那个下标,输出名字。

2.是否同构(same)

###  1.题目:

在这里插入图片描述

2.开始代码:

#include <iostream>
#include <stdio.h>
using namespace std;
const int N =1e6+10;
int swapp[N];
bool canSwap(int a[],int b[],int n) {
    for (int k = 0; k <= n / 2; ++k) {
        for (int i = 0; i < k; ++i) {
            swapp[i] = a[n - k + i]; 
            swapp[n - k + i] = a[i];
        }
        for (int i = k; i < n - k; ++i) {
            swapp[i] = a[i];
        }
        bool f = true;
        for (int i = 0; i < n; ++i) {
            if (swapp[i] != b[i]) {
                f = false;
                break;
            }
        }
        if (f) {
            return true;
        }
    }
    return false;
}
int a[N], b[N];
int main() {
	freopen("same.in","r",stdin);
	freopen("same.out","w",stdout);
    int T;	
    scanf("%d",&T);
    while (T--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        for (int i = 0; i < n; ++i) {
            cin >> b[i];
        }
        if (canSwap(a, b, n)) {
            printf("Yes\n");
        } else {
            printf("No\n");
        }
    }
    fclose(stdin);
	fclose(stdout);
    return 0;
}

和暴力差不多,直接将每个点都当成k,开始交换,前面的和后面的位置直接换,还完后检查是否等于b数组。

3.正解

例子1::
#include<iostream>
#include<cstdio>
using namespace std;
int t,n,a[1000005],b[1000005]; 
bool fun(){
	for(int i=1;i<=n;i++){
		if(a[i]!=b[i]){
			return 0;
		}
	}
	return 1;
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		if(fun()){
			printf("Yes\n");
			continue;
		}
		int pos=1;
		for(int i=n/2+1;i<=n;i++){
			if(a[i]==b[1]){
				pos=i;
				break;
			}
		}
		for(int i=pos;i<=n;i++) swap(a[i],a[i-pos+1]);
		if(fun()) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}
例子2:

inline O2优化了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
inline int read(){//快读
	char ch=getchar();
	int res=0;
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';ch=getchar();
	}
	return res;
}
int a[maxn],b[maxn],n;
inline bool check(){
	for(int i=1;i<=n;i++){
		if(a[i]!=b[i])
			return false;
	}
	return true;
}
inline void solve(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	for(int i=1;i<=n;i++){
		b[i]=read();
	}
	if(check()){
		puts("Yes");
		return;
	}
	int pos=0;
	for(int i=n-(n/2)+1;i<=n;i++){
		if(a[i]==b[1]){
			pos=i;
			break;
		}
	}
	for(int i=pos;i<=n;i++){
		swap(a[i],a[i-pos+1]);
	}
	if(check()){
		puts("Yes");
	}else{
		puts("No");
	}
}
int main(){
	ios::sync_with_stdio(false);
	int T=read();
	while(T--){
		solve();
	}
	return 0;
}

思路:
枚举k这个点,遇见相同的值,就将这个点定义为k,然后交换前后位置,如果相等,就输出Yes,如果不同,就输出No

3.箱子(box)

1.题目

在这里插入图片描述

2.开始代码

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
priority_queue<long long> q;
long long n,m,x;
long long ans,sum;
int main(){
	freopen("box.in","r",stdin);
	freopen("box.out","w",stdout);
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&x);
		q.push(-x);
	}
	while(q.size()>1){
		sum=0;
		for(int i=1;i<=m&&!q.empty();i++){
			long long k=-q.top();
			sum+=k;
			q.pop();
		}
		q.push(-sum);
		ans+=sum;
	}
	printf("%lld",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

思路:
就是刚开始想的合并果子,然后突然发现和求哈夫曼编码一样的方式做,就直接大了一个优先队列,然后负数存储。求所有的值

3.正解

例子1:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	priority_queue<ll> pq;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		pq.push(-x);
	}
	if((n-1)%(m-1)>0){
		int cnt=m-1-(n-1)%(m-1);
		while(cnt--){
			pq.push(-0);
		}
	}
	ll ans=0;
	while(pq.size()>1){
		ll res=0;
		for(int j=1;j<=m;j++){
			res+=pq.top();
			pq.pop();
		}
		ans+=res;
		pq.push(-res);
	}
	cout<<ans<<endl;
	return 0;
}
/*
ios::sync_with_stdio(false) 是 C++ 标准库中 iostream 额外功能的一个设置,用于控制标准输入输出流 
(stdin 和 stdout) 是否同步。默认情况下,sync_with_stdio(true) 表示输入输出操作会同步,即从标准输
入读取数据的同时程序可能会阻塞等待用户输入。而当设置为 false 时,这种同步被解除,这样可以提高程序
的性能,特别是在处理大量输入或输出时,因为不需要等待 I/O 操作完成就能继续执行其他计算。如果你的应
用不需要频繁的用户交互,并且对速度有较高要求,设置 sync_with_stdio(false) 可能会更有益。然而,这
可能会导致在调试过程中,由于无法即时看到输入输出的结果而增加困惑,因此在生产环境中通常会选择禁用同
步。
*/
例子2:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
priority_queue<long long> q;
long long n,m,x;
long long ans,sum;
int main(){
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&x);
		q.push(-x);
	}
	if((n-1)%(m-1)>0){
		int cnt=m-1-(n-1)%(m-1);
		while(cnt--){
			q.push(-0);
		}
	}
	while(q.size()>1){
		sum=0;
		for(int i=1;i<=m&&!q.empty();i++){
			long long k=-q.top();
			sum+=k;
			q.pop();
		}
		q.push(-sum);
		ans+=sum;
	}
	printf("%lld",ans);
	return 0;
}

思路:和上面一样,就是哈夫曼编码的求法来打; 就多了一点:补0这种情况一定会发生一次当前剩余数量不够 m个的情况,那么我们可以补足若干个0 ,使得满足m,然后我们按照上述方法做即可(在第一次合并的时候少选几个箱子,以便让后面的合并可以每次都取 m个。)

4.社恐的聚会(party)

1.题目

在这里插入图片描述

2.开始代码:

#include <iostream>
#include <stdio.h>
using namespace std;
const int N =1e6+10;
int main() {
	int n;
	scanf("%d",&n);
	cout<<"Yes"<<endl;
	if(n%2==0){
		int ans=n/2;
		cout<<ans;
	}
	else{
		int ans=n/2+1;
		cout<<ans;
	}
    return 0;
}

思路:看样例,第一个位奇数,输出的是他/2再向上取整,而第三个为偶数,输出的是直接/2

正解:

例子1:
#include<bits/stdc++.h>
using namespace std;
const int maxn=525;
struct graph{
    int head[maxn],nxt[maxn*maxn],to[maxn*maxn],cnt;
    inline graph():cnt(1){}
    inline void add(int u,int v){
        nxt[++cnt]=head[u];
        to[cnt]=v;
        head[u]=cnt;
    }
}gr;
int n,g[maxn][maxn];
bool vis[maxn];
int color[maxn],sz[maxn][2],idx;
bool dfs(int u,int c){
    vis[u]=true;
    color[u]=c;
    sz[idx][c]++;
    for(int i=gr.head[u];i;i=gr.nxt[i]){
        int v=gr.to[i];
        if(vis[v]){
            if(color[u]==color[v]){
                return false;
            }
        }else{
            if(!dfs(v,c^1))
                return false;
        }
    }
    return true;
}
bool dp[maxn][maxn][2];
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>g[i][j];
        }
    }
    for(int i=1;i<n;i++){
        for(int j=i+1;j<=n;j++){
            if(!g[i][j]||!g[j][i]){
                gr.add(i,j);
                gr.add(j,i);
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(vis[i])
            continue;
        idx++;
        if(!dfs(i,0)){
            cout<<"No"<<endl;
            return 0;
        }
    }
    dp[0][0][0]=true;
    dp[0][0][1]=true;
    int mx=n/2;
    for(int i=1;i<=idx;i++){
        for(int j=sz[i][0];j<=mx;j++){
            dp[i][j][0]|=dp[i-1][j-sz[i][0]][0];
            dp[i][j][0]|=dp[i-1][j-sz[i][0]][1];
        }
        for(int j=sz[i][1];j<=mx;j++){
            dp[i][j][1]|=dp[i-1][j-sz[i][1]][0];
            dp[i][j][1]|=dp[i-1][j-sz[i][1]][1];
        }
    }
    int ans=0;
    for(int j=mx;j>=1;j--){
        if(dp[idx][j][0]||dp[idx][j][1]){
            ans=n-j;
            break;
        }
    }
    cout<<"Yes"<<endl;
    cout<<ans<<endl;
    return 0;
}

思路: 该程序的主要目标是检查一个图是否为二分图,并求解与其相关的最大团问题。下面详细介绍其实现思路和步骤。

  1. 输入和图的构建
    程序首先接收输入的邻接矩阵 g,表示图中节点之间的连接关系。矩阵的大小为 n x n,其中元素 g[i][j] 表示节点 i 和节点 j 之间的连接。当 g[i][j] 为零时,表示这两个节点没有直接连接。在此基础上,程序利用邻接表的形式构建无边图的结构,使用结构体 graph 来存储图的边信息。

  2. 深度优先搜索(DFS)与二分图检测
    通过 DFS 算法,程序对图进行着色,尝试将其分为两类(即二分)。具体地,对于每个未访问的节点 u,赋予颜色 0,并递归访问其相邻节点 v。如果 v 未被访问,则赋予相反的颜色(1),并继续递归;如果 v 已被访问且颜色与 u 相同,则说明图不是二分图,程序返回 false。

在 DFS 完成后,如果发现图是二分的,程序会记录下每个子图中各个颜色的节点数量,便于后续的动态规划计算。

  1. 动态规划(DP)求解最大团
    定义 DP 数组 dp[i][j][k],表示前 i 个子图中,选择 j 个节点且最后一个子图的颜色为 k 的可能性。初始化时,设置 dp[0][0][0] = true 和 dp[0][0][1] = true,表示未选节点的状态是有效的。

接下来,程序遍历每个子图,对于每种颜色的节点数,更新 DP 表。通过转移方程,将前一个子图的状态转移到当前子图,考虑选择不同数量的节点。最终,程序查找能满足条件的最大节点数,并记录未选节点的数量。

  1. 输出结果
    当通过 DP 表得到满足条件的最大节点组合后,程序输出 “Yes” 及其对应的最小未选节点数。如果在 DFS 阶段发现图不是二分图,则直接输出 “No”。

总结
该程序结合了图论的基本原理(如二分图检测)与动态规划技术,能高效解决图的最大团问题,同时确保程序逻辑清晰、结构合理。通过这种方式,程序不仅能够判断图的性质,还能在此基础上进行进一步的数据处理和结果输出,为处理更复杂的图问题提供了有效的思路和方法。

总结:

细节还需要改变,时间还需要考虑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aaronleoesr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值