4月11日省赛,给自己加油!在bin神的交流群里认识了一位郑州轻工业学院的巨巨,可以面基了。。。一起去北京吧


国王的烦恼。前面连通的之后不连通的明显的用并查集处理啊。。。但是是使用逆序的并查集,即为之后不连通的,过了一天之后加上这个桥就连通了那么ans++。看看代码就懂了的。。。逆序并查集用的很多的,还有带权值并查集的使用。。食物链是个特别经典的题(当然也有不带权值的巧妙做法)

const int maxn=10050;
const int maxm=100050;

int fa[maxn],n,m;

struct node{
	int u,v,day;
}edge[maxm];

int cmp(node a,node b){
	return a.day>b.day;
}

int getf(int x){
	if (x==fa[x]) return x;
	return fa[x]=getf(fa[x]);
}

int main(){
	//freopen("input.txt","r",stdin);
	int i,j,k;
	int day=0,ans=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].day);
		day=max(day,edge[i].day);
	}
	for(i=1;i<=n;i++) fa[i]=i;
	i=1;
	sort(edge+1,edge+m+1,cmp);
	while(day>0){
		while(edge[i].day==day){
			int u=getf(edge[i].u),v=getf(edge[i].v);
			if (u!=v){
				fa[u]=v;
				ans++;
				break;
			}
			else i++;
		}
		while(edge[i].day==day){
			int u=getf(edge[i].u),v=getf(edge[i].v);
			fa[u]=v;
			i++;
		}
		day--;
	}
	printf("%d\n",ans);
	return 0;
}


网址寻路:看到题就知道又是dfs。。。蓝桥杯暴力方法万岁!细节:层数已经固定好了,为3!另外,起点和终点可以是相同的要特殊判断,而且vis数组不能修改标记


struct node{
	int to,next;
}edge[200050];

int n,m;
int tot=0,ans=0;
int head[100050];
int vis[100050];
int Print[10];

void addedge(int u,int v){
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}

void print(){
	for(int i=0;i<=3;i++) printf("%d%c",Print[i],i==3?'\n':' ');
}

void dfs(int start,int pos,int num){
	if (num==4){
		ans++;
		//print();方便自己调试,看到完整的路径过程 
		return;
	}
	int k;
	for(k=head[pos];k!=-1;k=edge[k].next){
		int v=edge[k].to;
		if (!vis[v]){
			vis[v]=1;
			Print[num]=v;
			dfs(start,v,num+1);
			vis[v]=0;
		}
		else if (v==start&&num==3){
			Print[num]=v;
			dfs(start,v,num+1);
			//跟上面的唯一区别是不用标记vis
			//因为已经判定过,下一步输出而且这个点就是起点所以不会导致死循环
			//而且vis也不能置0,置0会导致1-2-1-2的情况出现
			// 
		}
	}
}

int main(){
	//freopen("input.txt","r",stdin);
	int i,j,k,u,v;
	memset(head,-1,sizeof(head));
	memset(edge,0,sizeof(edge));
	memset(vis,0,sizeof(vis));
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d",&u,&v);
		addedge(u,v); 
		addedge(v,u);
		//较大的n和m值一般要学会放弃矩阵,用邻接表 
	}
	for(i=1;i<=n;i++){
		vis[i]=1;
		Print[0]=i;
		dfs(i,i,1);
		vis[i]=0;
	}
	printf("%d\n",ans);
	return 0;
}

最大子阵。比较难的想到的DP题。大家应该会前缀和的做法吧,如果是一维数组中求最大连续子序列和怎么做的。。就是把每个的前缀放进去然后循环一次相减就好了是吧。所以呢,把每行都求出前缀和。然后暴力各类连续行,然后去求单行的最大连续子序列和。比如行数为3,那么我需要求1,2,3,1和2,2和3,1和2和3,每个逗号分开,把这些行中的列号相对的数相加得到一个新行,求此行的最大。。。

const long long maxnum=550;
long long num[maxnum];
long long ans;
long long a[maxnum][maxnum];
long long n,m;

void calc(){
	long long i,j,k,l;
	long long temp=num[1],Max=num[1];
	for(i=2;i<=m;i++){
		if (temp>0) temp+=num[i];
		else temp=num[i];
		Max=max(Max,temp);
	}
	ans=max(ans,Max);
}

int main(){
	//freopen("input.txt","r",stdin);
	long long i,j,k,l;
	scanf("%I64d%I64d",&n,&m);
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
			scanf("%I64d",&a[i][j]);
	ans=-99999999;
	for(i=1;i<=n;i++){ 
		memset(num,0,sizeof(num));
		for(j=i;j<=n;j++){
			long long sum=0;
			for(k=1;k<=m;k++)
				num[k]+=a[j][k];
			calc();
		}
	}
	printf("%I64d\n",ans);
	return 0;
}

大臣的旅费:题意中废话去掉之后:求树的直径

方法:两次dfs。第一次:从任意点出发,找到离该点距离最大的点i;第二次,从i点出发,找到离i的最大距离点j

注意:本题数据量比较大,必须学会使用邻接表。。。矩阵实在太浪费空间了


int dis[10050][10050];
int vis[12000];
int n,i,j,k;
int ans=0;

void dfs(int pos,int dist){
	int i,j,k;
	ans=max(dist,ans);
	for(k=1;k<=n;k++)
		if (dis[pos][k]&&!vis[k]){
			vis[k]=1;
			dfs(k,dist+dis[pos][k]);
		}
}

int main(){
	freopen("input.txt","r",stdin);
	memset(dis,0,sizeof(dis));
	int u,v,cost;
	scanf("%d",&n);
	for(i=1;i<=n-1;i++){
		scanf("%d%d%d",&u,&v,&cost);
		dis[v][u]=dis[u][v]=cost;
	}
	for(i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		vis[i]=1;
		dfs(i,0);
	}
	//printf("%d\n",ans);
	printf("%d\n",ans*10+ans*(1+ans)/2);
	return 0;
}

波动数列:dp[i][j]考虑,其中j表示对n的余数

小朋友排队:跟逆序对一样的思路,二分的归并查找