HPU个人训练3

本文精选了CodeForces平台上的经典算法题,包括A-KefaandPark、B-CompleteTripartite、C-ShortestCycle等,详细解析了每道题目的解题思路与代码实现,覆盖了图论、搜索、数学等核心算法知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - Kefa and Park

题目链接:http://codeforces.com/problemset/problem/580/C

题目大意:kefa住在编号为1的房子里,现在kefa要去餐厅,餐厅位于树的叶子节点,现在给一个m,如果kefa去餐厅的路径连续遇到猫的个数大于m,该餐厅kefa就不会过去,1位置上有猫。问kefa可以去的餐厅个数。

解题思路:用dfs将图跑一遍即可,过程中标记连续遇到猫的个数,还有一个点就是需要判断该点是否为叶子节点。最好不要用链式前向星,该方法判断叶子节点不好判断。

Code:

#include<iostream>
#include<vector>

using namespace std;
const int N = 1e6+5;
vector<int>ve[N];
int n,m,st[N],ans;

void dfs(int u,int k,int pr){//分别表示当前节点,连续猫的个数,父亲节点
    if(k>m) return ;
    int num=ve[u].size();
    if(num==1&&u!=1){
		ans++;
		return ; 
	} 
    for(int i=0;i<num;i++){
        int v=ve[u][i];
        if(v==pr) continue;
        if(st[v]) dfs(v,k+1,u);
        else dfs(v,0,u);
    }
}

int main(){
    ans=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>st[i];
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    dfs(1,st[1],0);
    cout<<ans<<endl;

    return 0;
}

B - Complete Tripartite

题目链接:http://codeforces.com/problemset/problem/1228/D

题目大意:问能否将一个图分成三个集合,每个集合内部的点没有边,每个集合的点都和另外两个集合的点连有边,每个集合不能为空。

解题思路:先将1放在集合1,然后将和1相连的点全部置为2,剩下的点自然就是集合1里面的元素,然后再从集合2中随意找一个元素,遍历和它相连的点,如果该点不属于集合1,就将该点放在集合3中。这样就将三个集合找出来了。最后判断三个集合是否与题意有冲突即可。

Code:


#include<iostream>
#include<cstring>
#include<vector>

using namespace std;
const int N = 6e5+5,M=N*2;
typedef long long ll;
ll h[N],cnt;
ll n,m,color[N],in[N];
vector<ll>v[4];

struct Edges{
    ll to,nxt;
}eg[M];

void add(ll u,ll v){
    eg[cnt].to=v;
    eg[cnt].nxt=h[u];
    h[u]=cnt++;
}

int main(){
    memset(h,-1,sizeof(h));
    cnt=0;
    cin>>n>>m;
    for(ll i=0;i<m;i++){
        ll u,v;
        cin>>u>>v;
        in[u]++;in[v]++;//表示每个点和多少边相连
        add(u,v);add(v,u);
    }
    // cout<<"***********"<<endl;
    color[1]=1;
    for(ll i=h[1];i!=-1;i=eg[i].nxt){//先把和1相连的边的点都置为2
        ll v=eg[i].to;
        color[v]=2;
    }
    ll pos=-1;
    for(ll i=1;i<=n;i++){//剩下的点的颜色都置为1
        if(color[i]!=2) color[i]=1;
        else if(pos==-1){
            pos=i;
        }
    }
    if(pos==-1){
        cout<<-1<<endl;
        return 0;
    }
    for(ll i=h[pos];i!=-1;i=eg[i].nxt){//和颜色为2的点相连的点颜色不为1就一定为3
        ll v=eg[i].to;
        if(color[v]==1) continue;
        color[v]=3;
    }
    ll len1,len2,len3;
    for(ll i=1;i<=n;i++){
        if(color[i]==1) v[1].push_back(i);
        else if(color[i]==2) v[2].push_back(i);
        else v[3].push_back(i);
    }
    bool flag=0;
    len1=v[1].size();len2=v[2].size();len3=v[3].size();
    // cout<<"*************\n";
    if(len1==0||len2==0||len3==0){//判断是否有集合为空
        cout<<-1<<endl;
        return 0;
    }
    for(ll i=0;i<len1;i++){
        if(in[v[1][i]]!=len2+len3){//如果没有和集合2,3所有的点相连,就不符合条件
            flag=1;
            break;
        }
        for(int j=h[v[1][i]];j!=-1;j=eg[j].nxt){//如果集合内部存在边,不符合条件
            int v=eg[j].to;
            if(color[v]==1) {
                flag=1;
                break;
            }
        }
    }
    // cout<<"*************\n";
    // cout<<len2<<endl;
    // for(int i=1;i<=n;i++){
    //     cout<<h[i]<<endl;
    // }
    for(ll i=0;i<len2;i++){
        if(in[v[2][i]]!=len1+len3){
            flag=1;
            break;
        }
        for(int j=h[v[2][i]];j!=-1;j=eg[j].nxt){
            int v=eg[j].to;
            if(color[v]==2) {
                flag=1;
                break;
            }
            // cout<<j<<endl;
        }
    }
    // cout<<"*************\n";
    if(flag){
        cout<<-1<<endl;
        return 0;
    }
    for(ll i=0;i<len3;i++){
        if(in[v[3][i]]!=len1+len2){
            flag=1;
            break;
        }
        for(int j=h[v[3][i]];j!=-1;j=eg[j].nxt){
            int v=eg[j].to;
            if(color[v]==3) {
                flag=1;
                break;
            }
        }
    }
    // cout<<"*************\n";
    if(flag){
        cout<<-1<<endl;
        return 0;
    }
    else {
        for(ll i=1;i<=n;i++){
            if(i==1) cout<<color[i];
            else cout<<" "<<color[i];
        }
        cout<<endl;
    }

    return 0;
}

C - Shortest Cycle

题目链接:http://codeforces.com/problemset/problem/1206/D

题目大意:两个数与运算不为0,那么就存在一条边,判断该图中是否存在有环,存在输出最小环的长度,反之输出-1

解题思路:根据抽屉原理,若n大于128,则最小环一定为3,反之跑floyd求最小环即可。

Code:

#include<iostream>

using namespace std;
const int N = 1e5+5;
typedef long long ll;
const ll inf = 1e14;
ll a[N],dis[300][300],e[300][300];
int n;

ll floyd(){
    ll res=inf;
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j||i==k||j==k) continue;
                res=min(res,dis[i][j]+e[j][k]+e[k][i]);
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            }
        }
    }
    return res==inf?-1:res;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==0){
            i--;
            n--;
        }
    }
    if(n>128){
        cout<<3<<endl;
    }
    else {
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i]&a[j] && i!=j){
                    dis[i][j]=e[i][j]=1;
                }
                else {
                    dis[i][j]=e[i][j]=inf;
                }
            }
        }
        cout<<floyd()<<endl;
    }

    return 0;
}

D - Alex and a Rhombus

题目链接:http://codeforces.com/problemset/problem/1180/A

题目大意:思维题,根据图中规律,给出第n次操作图中小正方形的个数

解题思路:手动模拟可以得出,sn=n*(n-1)*2+1.

Code:

#include<iostream>

using namespace std;

int main(){
	int n;
	cin>>n;
	cout<<n*(n-1)*2+1<<endl;
	
	return 0;
}

E - Nick and Array

题目链接:http://codeforces.com/problemset/problem/1180/B

题目大意: 给你一个数组,每次操作形式为在数组中任意挑选几个数,每个数转换形式为ai=-ai-1;问怎样转换,使数组乘积最大。每个数都可多次操作。

解题思路:对于正数和0,转化为负数绝对值大于本身,明白这个后就简单了。
如果数组元素个数为偶数,全部转化为负数即可。
如果数组元素个数为奇数,将所有元素转化为负数后,将绝对值最大的再进行一次操作即可

Code:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+5;
int a[N],b[N],c[N],n,num1,num2,num3;
int main(){	
	cin>>n;	
	for(int i=1;i<=n;i++){		
		cin>>a[i];		
	}	
	int minn=0,temp;	
	if(n%2==0){		
		for(int i=1;i<=n;i++){			
			if(a[i]>=0) a[i]=-a[i]-1;	
		}	
	}	
	else if(n&1){		
		for(int i=1;i<=n;i++){			
			if(a[i]>=0) a[i]=-a[i]-1;
			if(a[i]<minn) {
				minn=a[i];
				temp=i;
			}
		}
		a[temp]=-a[temp]-1;	
	}	
	for(int i=1;i<=n;i++){		
		if(i==1) cout<<a[i];		
		else cout<<" "<<a[i];	
	}	
	cout<<endl;		
return 0;}

F - Valeriy and Deque

题目链接:http://codeforces.com/problemset/problem/1180/C

题目大意:每次操作将数组头两个数取出,然后将值大的放入数组头,值小的放在数组尾,问每次操作所取出来的两个数分别是多少。

解题思路:用到一种新函数,deque,双向队列,和队列操作差不多,多了从队头队尾取出加入元素的操作。找到数组中最大数后找循环节,对循环之前和进入循环后分别处理即可。另外这题也可以暴力写,毕竟有6s,每次都有这种大佬,暴力大法,无所不A。

Code:

#include<iostream>
#include<deque>

using namespace std;
const int N = 3e5+5;
typedef long long ll;
int n,m,maxx=0,num=0,ans[N];
deque<ll>q;

struct node {
	int l,r;
}t[N];

void solve(){
	while(1){
		int x=q.front();q.pop_front();
		int y=q.front();q.pop_front();
		if(x==maxx){
			q.push_front(y);
			for(int i=1;i<=n-1;i++){
				y=q.front();q.pop_front();
				ans[i]=y;
			}
			break;
		}
		else {
			num++;
			t[num].l=x;t[num].r=y;
			if(x>y){
				q.push_front(x);
				q.push_back(y);
			} 
			else {
				q.push_front(y);
				q.push_back(x);
			}
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) {
		int x;
		cin>>x;
		maxx=max(maxx,x);
		q.push_back(x);
	}
	solve();
	for(int i=0;i<m;i++){
		ll c;
		cin>>c;
		if(c<=num) cout<<t[c].l<<" "<<t[c].r<<endl;
		else {
			c-=num;
			c%=(n-1);
			if(c==0) c=n-1;//如若刚好乘除,此时c为循环节最后一个元素
			cout<<maxx<<" "<<ans[c]<<endl;
		}
	}
	
	return 0;
}

G - Circle Metro

题目链接:http://codeforces.com/problemset/problem/1169/A

题目大意:A,B坐两个列车坐到某个目标位置,列车循环转,问A,B在下车之前是否会出现在同一个站点

解题思路:sb题,枚举即可

Code:

#include<iostream>

using namespace std;
int n,a,x,b,y;

int main(){
	cin>>n>>a>>x>>b>>y;
	bool flag=0;
	if(a==b){
			flag=1;
	}
	while(a!=x&&b!=y){
		a=a+1;b=b-1;
		if(a==n+1) a=1;
		if(b==0) b=n;
		if(a==b){
			flag=1;
			break;
		}
	}
	if(flag) cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
	
	return 0;
}

H - Pairs

题目链接:http://codeforces.com/problemset/problem/1169/B

题目大意:找到两个数x,y;在给出的数组中至少有一个数和x或y相等。

解题思路:在第一行的两个数一个数为x,在剩下的里面找y。看是否满足条件。

Code:

#include<iostream>
#include<map>

using namespace std;
const int N = 3e5+5;
typedef long long ll;
ll n,m;
struct node {
	ll x,y;
}num[N];
map<ll,ll>ma1,ma2;
ll x1,x2,sum1,sum2,x,y;

int main(){
	sum1=1;sum2=1;
	cin>>n>>m;
	cin>>x1>>x2;
	for(ll i=2;i<=m;i++) {
		cin>>x>>y;
		if(x!=x1&&y!=x1){
			ma1[x]++;
			ma1[y]++;
		}
		else sum1++;
		if(x!=x2&&y!=x2){
			ma2[x]++;
			ma2[y]++;
		}
		else sum2++;
	}
//	cout<<sum1<<endl
	bool flag=0;
	for(int i=1;i<=n;i++){
		if(i!=x1){
			if(sum1+ma1[i]>=m){
				cout<<"YES"<<endl;
				flag=1;
				break;
			} 
		}
		if(i!=x2){
			if(sum2+ma2[i]>=m){
				cout<<"YES"<<endl;
				flag=1;break;
			} 
			
		}
	}
//	cout<<"***********\n";
	if(!flag) cout<<"NO"<<endl;
	
	return 0;
}

I - Increasing by Modulo

题目链接:http://codeforces.com/problemset/problem/1169/C

题目大意:将一个数组变为非递减序列问最少操作次数

解题思路:二分操作次数求最小,先将last值设为0,遍历一遍数组,如果a[i]大于last,看在当前操作次数下是否可以将a[i]转化为last,如果不能则更新last=a[i],如果a[i]小于last,看在当前操作次数下将a[i]变得大于等于last,不行则说明当前情况不成立。

Code:

#include<iostream>

using namespace std;
const int N = 3e5+5;
int n,m,a[N];

bool check(int x){
	int now=0;
	for(int i=1;i<=n;i++){
		if(now<a[i]){
			if(m-a[i]+now>x) now=a[i];
		}
		else if(now>a[i]){
			if(a[i]+x<now) return 0;
		}
	}
	return 1;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	int l=0,r=1e9,mid,ans=1e9;
	while(l<r){
		mid = (l+r)>>1;
		if(check(mid)){
			r=mid;
			ans=min(ans,mid);
		}
		else l=mid+1;
	}
	cout<<ans<<endl;
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值