1.NOIP模拟赛试题详讲

A

题目描述: 给一个 n 个节点 m 条边的无向图, 有 k 轮操作, 每轮操作是选择尽量多的边删除, 如果有多种方案, 那么选择边权和最大的那个, 但是要求删除的边中不存在环.对于每条边, 输出它在第几次操作被删除, 如果这条边最后都没有被删除那么输出 0.

先画个图来看看想法
在这里插入图片描述
红色的为这一轮所选删除的边
在这里插入图片描述
黄色为第2轮所删除的边。
最后剩了边权为1的这条边。

我们再观察一下,每次删除的就是一棵树,因为树上再加一条边就是环了,所以其实就是每次找最大生成树。
那应该怎么找呢?
本蒟蒻只搞出了暴力枚举每轮k,每轮中库鲁斯卡尔找最大生成树。

#include<bits/stdc++.h>
using namespace std;

#define num ch-'0'
void get(int &res)
{
    
    
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

const int N=1e3+5,M=3e5+5,K=1e4+5;
int far[N][K],n,m,k,h[N],Num[N][N];
struct node 
{
    
    
	int x,y,w,id;
}e[M];

inline bool comp(node u,node v)
{
    
    
	return u.w>v.w;
}

inline int getfar(int x,int t)
{
    
    
	return far[x][t]==x? x:far[x][t]=getfar(far[x][t],t);
}

int main()
{
    
    
	get(n);get(m);get(k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=k;j++) far[i][j]=i;
	for(int i=1;i<=m;i++)
	{
    
    
		get(e[i].x);get(e[i].y);get(e[i].w);
		e[i].id=i;
	}
	sort(e+1,e+m+1,comp);
	for(int i=1;i<=m;i++)
	{
    
    
		int u=e[i].x,v=e[i].y;
		int t=Num[u][v]+1,f1=getfar(u,t),f2=getfar(v,t);
		while(f1==f2 && t<k)
		{
    
    
			t++;
			f1=getfar(u,t);
			f2=getfar(v,t);
		}
		if(f1==f2) continue;
		h[e[i].id]=t;
		far[f2][t]=f1;
		Num[u][v]=Num[v][u]=t;
	}
	for(int i=1;i<=m;i++)
	{
    
    
		cout<<h[i]<<endl;
	}
}

然后居然A了70,电脑太好了吧

好了,下面是正解。(来自于我们机房的大佬)
也是用最大生成树,但我们开并查集时用far[N][K]来维护每一轮的并查集。
假如我已经在第一轮将2个点n,m合在一起了,在继续枚举,发现新的2个点a,b的祖先在第一轮相等,说明这2个点第一轮已经合了,就跳到下一轮,继续判断。
我们发现这个过程可以优化,不需要每次从第一轮开始,如果这两个点上个状态是第几轮,那么这次这2个点就肯定要在上一个的轮数+1,因为上一轮我们就已经跳了到,这一轮肯定也要跳,不如直接加。(太巧妙了) 所以其中最重要的是int t=past[u][v]+1,如果改为int t=1那时间复杂度就会和上面的代码差不多。

#include<bits/stdc++.h>
using namespace std;

#define num ch-'0'
void get(int &res)
{
    
    
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

const int N=1005,M=3e5+5,K=1e4+5;
int n,m,k,far[N][K],past[N][N];
int vis[M];
struct node
{
    
    
	int x,y,w,id;
}e[M];

inline comp(node x,node y)
{
    
    
	return x.w>y.w;
}

inline int getfar(int x,int t)
{
    
    
	return far[x][t]==x?x:far[x][t]=getfar(far[x][t],t);
}

int main()
{
    
    
	get(n);get(m);get(k);
	for(int i=1;i<=m;i++)
	{
    
    
		get(e[i].x);get(e[i].y);get(e[i].w);
		e[i].id=i;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=k;j++)
			far[i][j]=i;    //初始化,初始化,初始化
	sort(e+1,e+m+1,comp);//贪心
	for(int i=1;i<=m;i++)
	{
    
    
		int u=e[i].x,v=e[i].y;
		int t=past[u][v]+1,f1=getfar(u,t),f2=getfar(v,t);
		//因为是线性的,这2个点可以直接从过去状态开始,减少了大量的循环次数 
		while(f1==f2 && t<k)//在这个并查集中祖先相同,找下一个 
		{
    
    
			t++;
			f1=getfar(u,t);
			f2=getfar(v,t);
		}
		if(f1==f2) continue;
		vis[e[i].id]=t;
		far[f2][t]=f1;
		past[u][v]=past[v][u]=t; 
	} 
	for(int i=1;i<=m;i++)
		cout<<vis[i]<<endl;
	return 0;
}

B

题目描述:有一个n×n的黑白棋盘. 你需要用一些操作将整个棋盘变成全黑.一次操作首先选择一行i, 一列j, 记c1,c2,·· ·,cn为(i,1),(i,2),·· ·,(i,n) 的颜色. 之后将(1,j),(2,j),·· ·,(n,j)的颜色对应涂成c1,c2,·· ·,cn.求最少的操作次数. 如果无法成功, 输出−1.其中’#‘表示黑色,’.'表示白色.

我们多画点图可以发现:
1.当且仅当没有黑色的时候,做不出,输出-1;
2.只要把一行全部转换为了黑的,那么就能直接将全部列都完成,其中只需特判一下是否一列都是黑,在最后减掉即可;
3.为了做出一行为黑,我们又发现,如果在从左上到右下的对角线上的黑色,可以直接在它的行上造,所以这种情况的就这一行的白色数;
4.如果在其他位置的黑色,那么只能在不在它的其他行上造黑色,所以这个情况就是对应行的白色数+1;

综上,结果等于ans+n-cnt(造一行黑色的最小代价+去造每一列-一列全是黑的情况)

#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,num[N];
bool w=1,list1[N],list2[N];
    //特判是否全白,是否一列全是黑的,是否一列有黑的 
int main()
{
    
    
	char x;
	scanf("%d",&n);
	memset(list1,1,sizeof(list1));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
    
    
			cin>>x;
			if(x=='#')
			{
    
    
				w=0;
				list2[j]=1;
			}
			else
			{
    
    
				num[i]++;
				list1[j]=0;
			}
		}
		
	if(w) 
	{
    
    
		cout<<-1;
		return 0;
	}
	int ans=0x3f3f3f3f,cnt=0;
	for(int i=1;i<=n;i++)
	{
    
    
		if(list2[i]) ans=min(ans,num[i]);
		else ans=min(ans,num[i]+1);
		if(list1[i]) cnt++;
	}
	cout<<n+ans-cnt;
	return 0;
}

其余2题本蒟蒻不会,会了就更qwq

猜你喜欢

转载自blog.csdn.net/pigonered/article/details/120810694