模拟赛三补题报告
李智航
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;
}
思路: 该程序的主要目标是检查一个图是否为二分图,并求解与其相关的最大团问题。下面详细介绍其实现思路和步骤。
-
输入和图的构建
程序首先接收输入的邻接矩阵 g,表示图中节点之间的连接关系。矩阵的大小为 n x n,其中元素 g[i][j] 表示节点 i 和节点 j 之间的连接。当 g[i][j] 为零时,表示这两个节点没有直接连接。在此基础上,程序利用邻接表的形式构建无边图的结构,使用结构体 graph 来存储图的边信息。 -
深度优先搜索(DFS)与二分图检测
通过 DFS 算法,程序对图进行着色,尝试将其分为两类(即二分)。具体地,对于每个未访问的节点 u,赋予颜色 0,并递归访问其相邻节点 v。如果 v 未被访问,则赋予相反的颜色(1),并继续递归;如果 v 已被访问且颜色与 u 相同,则说明图不是二分图,程序返回 false。
在 DFS 完成后,如果发现图是二分的,程序会记录下每个子图中各个颜色的节点数量,便于后续的动态规划计算。
- 动态规划(DP)求解最大团
定义 DP 数组 dp[i][j][k],表示前 i 个子图中,选择 j 个节点且最后一个子图的颜色为 k 的可能性。初始化时,设置 dp[0][0][0] = true 和 dp[0][0][1] = true,表示未选节点的状态是有效的。
接下来,程序遍历每个子图,对于每种颜色的节点数,更新 DP 表。通过转移方程,将前一个子图的状态转移到当前子图,考虑选择不同数量的节点。最终,程序查找能满足条件的最大节点数,并记录未选节点的数量。
- 输出结果
当通过 DP 表得到满足条件的最大节点组合后,程序输出 “Yes” 及其对应的最小未选节点数。如果在 DFS 阶段发现图不是二分图,则直接输出 “No”。
总结
该程序结合了图论的基本原理(如二分图检测)与动态规划技术,能高效解决图的最大团问题,同时确保程序逻辑清晰、结构合理。通过这种方式,程序不仅能够判断图的性质,还能在此基础上进行进一步的数据处理和结果输出,为处理更复杂的图问题提供了有效的思路和方法。
总结:
细节还需要改变,时间还需要考虑