10.16考試總結

storms11發表於2024-10-18

T1 最小生成樹

題面:給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵恰好有 \(need\) 條白色邊的
權值和最小的生成樹。題目保證有解。
題解:
最小生成樹,自然而然想到kruskal演算法,但我們要讓白點恰好有 \(need\) 條,在白點多的時候,要多選黑點,在白點少的時候,要多選白點,所以我們可以根據白點選擇情況對黑點的權值進行調整,在白點多的時候,減少黑點權值,在白點少的時候,增加黑點權值,然後發現隨著黑點權值增加量的增加,白點數量單調遞增,所以考慮二分。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,k,fa[N],ans,need,sum,cnt;
struct node
{
	int u,v,w,cl;
}bi[N];
int find(int x)
{
	if(x==fa[x])return x;
	else return fa[x]=find(fa[x]);
}
bool cmp(node x,node y)
{
	if(x.w!=y.w)return x.w<y.w;
	else return x.cl<y.cl;
}
void kus()
{
	sum=0,cnt=0,need=0;
	for(int i=1;i<=n;i++)fa[i]=i;	
	for(int i=1;i<=m;i++)
	{
		int f1=find(bi[i].u),f2=find(bi[i].v);
		if(f1==f2)continue;
		cnt++;
		if(bi[cnt].cl==0)need++;
		fa[f2]=f1;
		sum+=bi[i].w;
		if(cnt==n-1)return;
	}
}
int main()
{
	freopen("e.in","r",stdin);
	freopen("e.out","w",stdout);	
	ios::sync_with_stdio(0); 
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++)
		cin>>bi[i].u>>bi[i].v>>bi[i].w>>bi[i].cl;
	int l=-1005,r=1005;
	while(l<r)
	{
		int mid=l+(r-l)/2;
		for(int i=1;i<=m;i++)if(bi[i].cl==0)bi[i].w+=mid;
		sort(bi+1,bi+m+1,cmp);
		kus();
		for(int i=1;i<=m;i++)if(bi[i].cl==0)bi[i].w-=mid;
		if(need>=k)ans=sum-mid*k,l=mid+1;
		else r=mid;
	}
	cout<<ans<<endl;
	return 0;
}

反思:做過這道題,甚至不止一次,但考場上沒做出來,還是自己學的不太好。

T2 矩陣遊戲

反向思考,一個正對角線上都有黑點,每個黑點都來自原圖的某一行某一列,因為交換操作,同一列同一行的點始終在同一列同一行。所以原圖的這一行和這一列繫結起來貢獻一個點在對角線上,所以對每個黑點行列連邊。跑二分匹配圖,有完美匹配就有解,否則無解。

#include <bits/stdc++.h>
using namespace std;
const int M=4e4+10,N=4e4+10;
int t,n,tot,nxt[M],go[M],hd[N],girl[N],ans;
bool vis[N];
void add(int x,int y)
{
	nxt[++tot]=hd[x];go[tot]=y;hd[x]=tot;
	return ;
}
bool find(int x)
{
	for(int i=hd[x];i;i=nxt[i])
	{
		int v=go[i];
		if(vis[v])continue;
		vis[v]=1;
		if(!girl[v]||find(girl[v]))
		{
			girl[v]=x;
			return 1;
		}
	}	
	return 0;
} 
int main()
{
	freopen("f.in","r",stdin);
	freopen("f.out","w",stdout);	
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--)
	{
		tot=0,ans=0;
		memset(hd,0,sizeof(hd)); 
		memset(girl,0,sizeof(girl));
		cin>>n;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				int x;cin>>x;
				if(x==1)add(i+n,j);
			}
		for(int i=1+n;i<=2*n;i++)
		{
			ans+=find(i);
			for(int i=1;i<=n;i++)vis[i]=0;
		}
		if(ans==n)cout<<"Yes"<<'\n';
		else cout<<"No"<<'\n';
	} 
	return 0;
} 

反思:想到了做法,同時寫出了程式,但因為一開始遇到不符合條件就輸出No,所以有地方沒清零,掛了20分,以後每次輸出清零後的陣列檢查。

T3 貪吃蛇

爆搜加一種alpha-beta剪枝,直接做。

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 25
#define CT 51
#define inf 0x3f3f3f3f
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int grid[maxn][maxn],n,m;
int dfs(int rnd,int player,int alpha,int beta,int fx,int fy,int px,int py)
{
	int flag=0,ans;
	if(player==1)ans=beta;
	else ans=alpha;
	for(int i=0;i<4;i++)
	{
		int x=fx+dx[i],y=fy+dy[i];
		if(x&&y&&x<=n&&y<=m&&!grid[x][y])
		{
			flag=1;
			int cur;
			grid[x][y]=player;
			if(player==1)cur=dfs(rnd+1,2,alpha,ans,px,py,x,y);
			else cur=dfs(rnd+1,1,ans,beta,px,py,x,y);
			grid[x][y]=0;
			if(player==2&&cur<=beta)return beta;
			if(player==1&&cur>=alpha)return alpha;
			if(player==1)ans=max(ans,cur);
			else ans=min(ans,cur);
		}
	}
	if(!flag)
	{
		if(player==1)return -CT+rnd;
		else return CT-rnd;//提示:如果自己能贏,一定會盡量快地贏;如果自己會輸,一定會死得儘量晚.
	}
	return ans; 
}
int main()
{
	freopen("h.in","r",stdin);
	freopen("h.out","w",stdout);
	scanf("%d%d",&n,&m);
	int x1,y1,x2,y2;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=m; j++)
		{
			scanf("%d",&grid[i][j]);
			if(grid[i][j]==1) x1=i,y1=j;
			if(grid[i][j]==2) x2=i,y2=j;
		}
	int ans=dfs(1,1,inf,-inf,x1,y1,x2,y2); 
	if(ans<0) printf("2 %d\n",ans+CT);
	else  printf("1 %d\n",CT-ans);
	return 0;
}

反思:至少應該把爆搜寫出來。

T4 資訊攔截

挖坑。

相關文章