2.6C语言学习

本文介绍了四个编程题目,涉及朴素算法寻找最大正方形、动态规划解决封印问题、并查集在集合合并中的应用以及并查集与背包问题的结合,展示了不同技术在实际问题中的解决方案。
摘要由CSDN通过智能技术生成

P1387 最大正方形

一道提高减的题目,但是做法却比较朴素,遍历找最大值就能得出结果,看了题解的dp和各种前缀和做法顿时感觉自愧不如,不过能做出来的方法就是好方法,下面是我的代码

#include<bits/stdc++.h>
using namespace std;
int a[109][109];
int ans;
void find(int x,int y){
	int cnt=1;
	while(1){
		for(int i=x;i<=x+cnt;i++){
			for(int j=y;j<=y+cnt;j++){
				if(a[i][j]==0)goto loop;
			}
		}
		cnt++;
	}
	loop:
	ans=max(ans,cnt);
}
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]==1){
				find(i,j);
			}
		}
	}
	printf("%d",ans);
	return 0;
}

P1934 封印

一道动态规划的题目

由于题目具有无后效性,所以想到用DP来解决。

我们令f[i]表示打破前i层封印消耗元气的最小值,则状态转移方程如下:

f[i]=max⁡{f[i−1]+a[i]*n^2,f[k]+(a[k+1]+a[i])*sum(k+1,i)|0<k+1<i,a[k+1]+a[i]≤k}

状态转移方程写好后,问题在于求sum(k+1,i)时如果遍历一遍需要O(n)的复杂度。这样总复杂度为k(n^3),50-70分。

这个复杂度可以用预处理前缀和的方法来优化。用S[i]表示从a[1]到a[i]的

总和,则sum(k+1,i)=S[i]-S[k]。这样总复杂度为k(n^2),可以通过所有测试点。

 感觉是一道很入门的动态规划,但我也花了不少时间才搞定

#include<bits/stdc++.h>
using namespace std;
long long a[1009],b[1009],dp[1009];
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	int k=n*n;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		b[i]=b[i-1]+a[i];
	}
	for(int i=1;i<=n;i++){
		long long ans=k*a[i]+dp[i-1];
		for(int j=1;j<i;j++){
			if(a[i]+a[j]>m)continue;
			ans=min(ans,(a[i]+a[j])*(b[i]-b[j-1])+dp[j-1]);
		}
		dp[i]=ans;
	}
	printf("%lld",dp[n]);
	return 0;
}

P3367 【模板】并查集

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 (摘自百度)


关于并查集和路径压缩:

现在我们假定 f[i] 表示第 i 个人的老大是谁。

现在我们有甲,乙,丙三个人(分别用 a, b, c 表示)

假设甲和乙打架了,甲做了丙的小弟。则有 f[a]=b

后来甲打赢了丙

那么丙就是甲的小弟了。有 f[c]=a

但是如果我们这样表示,丙不能直接知道甲

所以,我们必须直接让丙的大哥变成最大的老大。

这样说应该就能非常好理解并查集了

#include<bits/stdc++.h>
using namespace std;
int f[100009];
int find(int s){
	if(f[s]==s)return s;
	return f[s]=find(f[s]);
}//并查集的精华(找老大)
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	int a,b,c;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d",&a,&b,&c);
		if(a==1){
			f[find(b)]=find(c);
		}
		else{
			if(find(b)==find(c))printf("Y\n");
			else printf("N\n");
		}
	}
	return 0;
}

P1455 搭配购买

我们发现这道题背包的限制条件很简单,买了A就必须买B,而且关系是互相的(而不是买A必买B,买B不一定买A)

所以啊,我们就用并查集将“有关系”的物品合成一个大大的物品,然后一个基础的01背包DP。

找错的过程也是十分痛苦,弄了我一个小时。

#include<bits/stdc++.h>
using namespace std;
int f[10009],dp[5000001];
struct node{
	int cost;
	int value;
};
node a[10009];
int find(int k){
	if(f[k]==k)return k;
	return f[k]=find(f[k]);
}
int main(){
	int n,m,w;
	scanf("%d %d %d",&n,&m,&w);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&a[i].cost,&a[i].value);
	}
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	int x,y;
	for(int i=1;i<=m;i++){
		scanf("%d %d",&x,&y);
		f[find(x)]=find(y);
	}
	for(int i=1;i<=n;i++){
		if(f[i]!=i){
			a[find(i)].cost+=a[i].cost;
			a[find(i)].value+=a[i].value;
			a[i].cost=0;
			a[i].value=0;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=w;j>=a[i].cost;j--){
			dp[j]=max(dp[j],dp[j-a[i].cost]+a[i].value);
		}
	}
	printf("%d",dp[w]);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值