Codeforces Round #544 (Div. 3)

A - Middle of the Contest

题意:给出两个时间,保证在同一天,求中间时刻

思路:直接转换为一天的第多少分钟,求平均,再转换回小时:分钟的形式。

int t,n,m;
char buffer[10];
void init(){}
int main(){
	init();
	int t1,t2;
	scanf("%s",&buffer[0]);
	buffer[0]-='0';
	buffer[1]-='0';
	buffer[3]-='0';
	buffer[4]-='0';
	t1=(buffer[0]*10+buffer[1])*60+(buffer[3]*10+buffer[4]);
	
	scanf("%s",&buffer[0]);
	buffer[0]-='0';
	buffer[1]-='0';
	buffer[3]-='0';
	buffer[4]-='0';
	t2=(buffer[0]*10+buffer[1])*60+(buffer[3]*10+buffer[4]);

	t1=(t1+t2)/2;
	buffer[0]='0'+(t1/60)/10;
	buffer[1]='0'+(t1/60)%10;
	buffer[3]='0'+(t1%60)/10;
	buffer[4]='0'+(t1%60)%10;
	printf("%s",buffer);
	return 0;
}

B - Preparation for International Women's Day

题意:给 \(n\) 盒子,每个盒子有 \(d_{i}\) 个物品。对这些盒子两两配对,问最多有多少个盒子可以参与两两配对,满足两盒子中物品相加为 \(k\) 的倍数。

思路:将盒子内物品对 \(k\) 取模,若满足取模后的数相加为 \(k\) ,则配对后正好为 \(k\) 的倍数。然后循环看每个余数的 \(i\)\(k-i\) 的最小值,就是当前余数的最大匹配的数量。注意对\(k/2\)\(0\) 进行特判。

int num[105];//num[i]表示余数i出现的次数。
int t,n,m,k,tp;

int main(){
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		scanf("%d",&tp);
		num[tp%k]++;
	}	
	int ans=num[0]/2;
	for(int i=1;i<=k/2;i++){
		if(i!=k-i)ans+=min(num[i],num[k-i]);
		else ans+=num[i]/2;
	}
	printf("%d",ans*2);
	return 0;
}

C - Balanced Team

题意:给出 \(n\) 个人,每个人有各自的能力值 \(a_{i}\) ,现在要在 \(i\) 个中取尽量多的人构成一组,并且组内人最大能力值与最小能力值之差应不大于5,问组内人数最多是多少。

思路:

方法1:先对能力值进行排序,然后用双指针尺取。左指针枚举组内最低人的能力,右指针指向相对左指针来说,能取到的最高的人。

方法2:用map记录每一个能力值出现的人的次数,然后枚举每一个最低能力值,对每一个能力值 \(a_{i}\) ,求出\(mp[a_{i}]+mp[a_{i+1}]+mp[a_{i+2}]+mp[a_{i+3}]+mp[a_{i+4}]+mp[a_{i+5}]\)的值,并求出最值。

unordered_map<int,int> mp;
int t,n,m;
ll tp;
std::vector<int> stu;
void init(){}
int main(){
	init();
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%lld",&tp);
		if(mp[tp]==0)stu.push_back(tp);//每个能力值最后只求一次
		mp[tp]++;
	}
	ll ans=0;
	for(int i=0;i<stu.size();i++){
		tp=0;
		for(int j=0;j<=5;j++){
			tp+=mp[stu[i]+j];
		}
		ans=max(ans,tp);
	}
	printf("%lld\n",ans);
	return 0;
}

D - Zero Quantity Maximization

题意:

​ 给出 \(a_{i}\)\(b_{i}\) ,找到一个实数 \(d\) (注意不一定是整数),使得由 \(a_{i},b_{i}\) ,用 公式\(c_{i}=a_{i}\cdot d + b_{i}\) 生成出的数组\(c_{i}\) 中,\(c_{i} = 0\) 的数量最多。

思路:

​ 首先可由原式得当 \(d=-\frac{b_{i}}{a_{i}}\) 时,\(c_{i}=0\) 。故,我们可以求出所有的 \(\frac {b_{i}} {a_{i}}\) 值,统计出这些值中出现最多的那个值,则当\(d\) 等于出现次数最多的那个值时,满足\(d=-\frac{b_{i}}{a_{i}}\) 式的\(a_{i},b_{i}\) 最多,则 \(c_{i}\) 中 0 最多。由于每个分数可以唯一表示为最简分数,所以我们可以统计 \(<\frac{b_{i}}{gcd(a,b)},\frac{a_{i}}{gcd(a,b)}>\) 二元组来统计 \(\frac {b_{i}} {a_{i}}\),也可以用\(b_{i}\cdot 1e^9+a_{i}\)的单个longlong整数来统计。特别注意,对a=0,b=0需要特殊处理。当\(a=0\)\(b=0\) 时,无论 \(d\) 如何取值都满足,应当直接加入答案。当\(a\neq0\)\(b = 0\) 时,d无论取何值都不满足。当\(a\neq0\)\(b=0\)时,\(b\)无论是何值,都是等价的,应当统计对应成同一个\(d\)

ll t,n,m;

ll gcd(ll a,ll b){
    return a%b==0? b:gcd(b,a%b);
}
struct xy{
	ll up,down;

};
map <pair<ll,ll>,ll> mp;
void init(){}
ll a[maxn],b[maxn],up[maxn],down[maxn];
int main(){
	init();
	scanf("%lld",&n);
	for(ll i=0;i<n;i++){
		scanf("%lld",&a[i]);
	}
	for(ll i=0;i<n;i++){
		scanf("%lld",&b[i]);
	}
	ll ans=0,maxx=0;
	for(ll i=0;i<n;i++){
		pair<ll,ll> tp;
		if(a[i]==0&&b[i]==0){
			ans++;
			continue;
		}
		if(b[i]==0){
			tp.first=0;
			tp.second=1;
			maxx=max(maxx,++mp[tp]);
			continue;
		}
		if(a[i]==0){
			continue;
		}
		ll ggcd=gcd(b[i],a[i]);
		tp.first=b[i]/ggcd;
		tp.second=a[i]/ggcd;
		maxx=max(maxx,++mp[tp]);
	}
	printf("%lld\n",ans+maxx);
	return 0;
}

E - K Balanced Teams

题意:给出n个人,每个人有能力 \(a_{i}\) ,现在要选出一部分人分成至多 \(k\) 组,要求组内人的能力值只差小于等于 \(5\) ,问最多能将多少人划分为组内。

思路:二维dp,\(dp[i][j]\) 表示前 \(i\) 个人分了 \(k\) 组的情况下最多能将多少人划入组内。

首先给能力值进行排序,再进行dp。

转移方程为\(dp[i][j]=max(dp[i-1][j],dp[i-x][j-1]),(其中x需要满足a[i-x]-a[i]\le 5)\) 因为对能力值排序过了,所以可以用单调队列来动态维护\(dp[i-x][j-1]\)的值,也可以用其他方法。因为从尽量靠前转移过来,结果是最优的,所以可以只枚举从最尽量最左转移过来,维护一个左指针。因为左指针始终是单调的,即使直接扫,也能够达到O(n)的复杂度。最后找出 \(i=n\) 列中最大的dp值就是答案。

const int N=5001;
int a[N],b[N],dp[N][N],n,k;
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++){
        for(int j=i;j>=1;j--)
            if(a[j]>=a[i]-5)b[i]=j-1;
        for(int j=1;j<=k;j++)
            dp[i][j]=max(dp[i-1][j],dp[b[i]][j-1]+i-b[i]);
    }
    cout<<dp[n][k]<<endl;
}

F - Spanning Tree with Maximum Degree

题意:求出原图中的一个生成树,使得该生成树中最大度数的节点的度数是所有生成树中最大的。

思路:找出原图中度数最大的点,将这个点的所有边先全部加入生成树,保证度数最大,然后对剩下的边集求整个图的生成树。

typedef long long ll;
const int maxn=2e5+50;
const int maxm=1e5+50;
const ll inf=0x7fffffff;

int fa[maxn];
int find(int x){
    return (fa[x] == x) ? x : (fa[x] = find(fa[x]));
}
bool unio(int x, int y){
    int tx = find(x), ty = find(y);
    if (tx != ty) {
        fa[tx] = ty;
        return 1;
    }
    return 0;
}

std::vector< pair<int,int> > g,ans;

int t,n,m;

int in[maxn];
void init(int n,int m){
	for(int i=0;i<=n;i++){
		fa[i]=i;
		in[i]=0;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	init(n,m);
	int u,v,maxx=0,maxi=0;
	for(int i=0;i<m;i++){
		scanf("%d%d",&u,&v);
		g.push_back(make_pair(u,v));//图的所有边
        
        //统计度数
		in[u]++;
		in[v]++;
        
        //找出最大的度数的点。
		if(in[u]>maxx){
			maxx=in[u];
			maxi=u;
		}
		if(in[v]>maxx){
			maxx=in[v];
			maxi=v;
		}
	}
	for(int i=0;i<m;i++){
		if(g[i].first==maxi||g[i].second==maxi){
			if(unio(g[i].first,g[i].second))
				ans.push_back(g[i]);
		}//将最大的度数的点的所有边连起来。
	}
	for(int i=0;i<m;i++){
		if(unio(g[i].first,g[i].second)){
			ans.push_back(g[i]);//生下的边跑生成树连起来
		}
	}
	for(int i=0;i<ans.size();i++){
		printf("%d %d\n",ans[i].first,ans[i].second);
	}
	return 0;
}
posted @ 2020-09-04 00:01  长安大学ACM  阅读(140)  评论(0编辑  收藏  举报