zoukankan      html  css  js  c++  java
  • 暑期集训day23考试整理

    T1: 猫和狗

    题目大意:(k)个观众,每个观众喜欢一条猫或狗,讨厌一条猫或狗(前猫后狗或前狗后猫),问最多满足多少个观众

    1.暴力((25pts)

    考场分数:(5pts)
    原因:

    暴力枚举每个状态,一条猫或狗只有选和不选两种状态,(O(2^n))枚举就完事

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define int long long
    using namespace std;
    const int maxn=500+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,m,K,hate[maxn],like[maxn],black[maxn],ans,dclock,vis[maxn];
    signed main(){
    
    	n=read(),m=read(),K=read();
    	for(int i=1;i<=K;i++){
    		char ch1,ch3;
    		int ch2,ch4;
    		cin>>ch1>>ch2>>ch3>>ch4;
    		if(ch1=='C'){
    			if(!vis[ch2])vis[ch2]=++dclock;
    			if(!vis[ch4+n])vis[ch4+n]=++dclock;
    			like[i]=vis[ch2],hate[i]=vis[ch4+n];
    		}
    		if(ch1=='D'){
    			if(!vis[ch2+n])vis[ch2+n]=++dclock;
    			if(!vis[ch4])vis[ch4]=++dclock;
    			like[i]=vis[ch2+n],hate[i]=vis[ch4];
    		}
    	}
    	long long maxs=(1<<dclock)-1;
    	for(long long s=1;s<=maxs;s++){
    		memset(black,0,sizeof(black));
    		int sum=0;
    		for(int i=1;i<=dclock;i++){
    			if(s&(1LL*1<<i-1)){
    				for(int j=1;j<=K;j++){
    					if(like[j]==i)black[j]++;
    					if(hate[j]==i)black[j]--;
    				}
    			}
    		}
    		for(int i=1;i<=K;i++){
    			if(black[i]==1)sum++;
    		}
    		ans=max(ans,sum);
    	}
    	cout<<ans;
    	return 0;
    }
    
    

    2.二分图((100pts)

    万万想不到,(T1)竟然是二分图原题(然而最后放在二分图的练习的最后一道,压根就没做)

    由于一个观众,喜欢猫就讨厌狗,喜欢狗就讨厌猫

    所以可以逆向思维,用二分图把冲突的情况处理掉

    把喜欢这个动物的与讨厌这个动物的相连,跑二分图,最后答案为:(n-tot/2)(有(tot)个冲突,说明只能满足一半)

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=500+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;咕咕
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,m,K,ans,a[maxn][maxn],girls[maxn],vis[maxn];
    char s1[maxn][maxn],s2[maxn][maxn];
    vector<int> g[maxn];
    bool Find(int u){
    	for(int i=0;i<g[u].size();i++){
    		int v=g[u][i];
    		if(!vis[v]){
    			vis[v]=1;
    			if(!girls[v]||Find(girls[v])){girls[v]=u;return 1;}
    		}
    	}
    	return 0;
    }
    int main(){
    	n=read(),m=read(),K=read();
    	for(int i=1;i<=K;i++){
    		cin>>s1[i]>>s2[i];
    	}
    	for(int i=1;i<=K;i++){
    		for(int j=1;j<=K;j++){
    			if(!strcmp(s1[i],s2[j]))g[i].push_back(j),g[j].push_back(i);
    		t}
    	}
    	for(int i=1;i<=K;i++){
    		memset(vis,0,sizeof(vis));
    		if(Find(i))ans++;
    	}
    	cout<<K-ans/2;
    	return 0;
    }
    
    

    T2:旋转子段

    题目大意:翻转某个子段,使得翻转后整个序列中满足(a[i]==i)的个数最多

    1.(O(n^3)) 枚举((35pts)

    三维,第一维枚举区间长度,第二维枚举左右端点,第三维扫区间,柿子:(ans=max(sum[l-1]+sum[n]-sum[r]+num))(num)为区间内满足条件个数)

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=5000+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int sum[maxn],n,f[maxn],a[maxn],ans;
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++){
    		a[i]=read();
    		sum[i]=sum[i-1];
    		if(a[i]==i)sum[i]++;	
    	}
    	ans=sum[n];
    	if(sum[n]==n)return cout<<n,0;
    	int now=0;
    	for(int d=2;d<=n;d++){
    		for(int i=1,j;(j=d+i-1)<=n;i++){
    			now=sum[i-1]+sum[n]-sum[j];
    			for(int k=i;k<=j;k++){
    				if(a[k]==j+i-k)now++;
    			}
    			ans=max(ans,now);
    		}
    	}
    	cout<<ans;
    	return 0;
    }
    

    2.(O(n^2))枚举((65pts)

    这里已经用了一个特殊性质了:(l=min(a[i],i)) (r=max(a[i],i))

    因此三维枚举可以直接转化为:一维枚举中点,第二维左右扩展

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=5e5+50;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,a[maxn],sum[maxn],ans,l,r,cnt;
    int main(){
    	n=read();
    	for(register int i=1;i<=n;i++){
    		a[i]=read();
    		sum[i]=sum[i-1];
    		if(a[i]==i)sum[i]++;
    	}
    	for(register int i=1;i<=n;i++){
    		x=a[i];
    		l=min(x,i),r=max(i,x);
    		if(l==r)continue;
    		cnt=0;
    		while(l<=r){
    			if(l!=r){
    				if(a[l]==r)cnt++;
    				if(a[r]==l)cnt++;
    			}else if(l==r){
    				if(a[l]==l)cnt++;
    			}
    			l++,r--;
    		}
    		ans=max(ans,cnt+sum[min(x,i)]+sum[n]-sum[max(x,i)]);
    	}
    	cout<<ans<<endl;
    }
    
    

    3.桶+(STL)(100pts)

    再细细观察,又能得到第二个性质:如果存在(a[i]+i==a[j]+j),那么以(frac{i+j}{2})为中点,且翻转区间包含(i)(j),翻转后(i)(j)都能满足条件

    证明略了,干瞪眼观察得到

    因此,可以开个桶维护一下(a[i]+i),然后枚举每一个(a[i]+i),由之前的性质可得到左端点和右端点,然后像前两个一样搞它就完事了

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1e6+5,INF=0x3f3f3f3f;
    int n,sum[maxn],a[maxn],ans;
    vector<int> ve[maxn];
    bool cmp(int x,int y){return abs(2*x-a[x]-x)<abs(2*y-a[y]-y);}
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++){
    		cin>>a[i];
    		sum[i]=sum[i-1];
    		if(a[i]==i)sum[i]++;
    		ve[a[i]+i].push_back(i);
    	}
    	for(int i=2;i<=2*n;i++){
    		if(!ve[i].empty()){
    			sort(ve[i].begin(),ve[i].end(),cmp);
    			for(int j=0;j<ve[i].size();j++){
    				int l=ve[i][j],r=a[l];
    				if(l>r)swap(l,r);
    				ans=max(ans,sum[l-1]+sum[n]-sum[r]+j+1);
    			}
    		}
    	}
    	cout<<ans;
    	return 0;
    }
    
    

    3.走格子

    题目大意:一个矩阵,一堆能走的点,期间能在墙上开传送门,然后求从起点到终点的最短路线

    感觉是考试中较简单的一道题了,然而打的正解挂了(75pts),原因是没注意必须要在墙上开门,以为可以原地开门(其实就是没考虑全,毕竟很多时候要开传送门的格子旁边就是墙)

    乱搞就完事了,一个格子对上下左右能走的地方连边

    然后考虑传送门,由于传送门只能由墙边传到墙边,所以可以直接从当前格子往上下左右走,看最后停的位置,然后连边,边权为上下左右中最短的距离(+1),因为必须要走到墙边才能打传送门,如图

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int maxn=1000+5,maxe=6e6+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,m,ans=INF,tx,vis[maxe],ty,qx,qy,head[maxe],tot,dis[maxe],inq[maxe];
    char a[maxn][maxn];
    struct Edge{
    	int to,next,from,dis;
    }e[maxe];
    void Add(int x,int y,int z){
    	e[++tot].next=head[x];
    	e[tot].to=y;
    	e[tot].from=x;
    	e[tot].dis=z;
    	head[x]=tot;
    }
    void Spfa(int S){
    	for(int i=0;i<=n*m*2;i++)dis[i]=INF;
    	dis[S]=0;
    	queue<int> q;q.push(S);
    	while(!q.empty()){
    		int u=q.front();q.pop();vis[u]=0;//cout<<u<<endl;
    		for(int x=head[u];x;x=e[x].next){
    			int v=e[x].to;
    			if(dis[v]>dis[u]+e[x].dis){
    				dis[v]=dis[u]+e[x].dis;
    				if(!vis[v]){
    				//	if(++inq[v]>n*m)break;
    					q.push(v);vis[v]=1;
    				}
    			}
    		}
    	}
    	if(dis[(tx-1)*m+ty]==INF)puts("no");
    	else cout<<dis[(tx-1)*m+ty]<<endl;
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			cin>>a[i][j];
    			if(a[i][j]=='C')qx=i,qy=j,a[i][j]='.';
    			if(a[i][j]=='F')tx=i,ty=j,a[i][j]='.';
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(a[i][j]=='.'){
    				if(a[i+1][j]=='.')Add((i-1)*m+j,i*m+j,1);
    				if(a[i-1][j]=='.')Add((i-1)*m+j,(i-2)*m+j,1);
    				if(a[i][j+1]=='.')Add((i-1)*m+j,(i-1)*m+j+1,1);
    				if(a[i][j-1]=='.')Add((i-1)*m+j,(i-1)*m+j-1,1);
    			}
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(a[i][j]=='.'){
    				int x1=i,x2=i,y1=j,y2=j,diss=INF;
    				while(a[x1+1][j]=='.')x1++;
    				while(a[i][y1+1]=='.')y1++;
    				while(a[x2-1][j]=='.')x2--;
    				while(a[i][y2-1]=='.')y2--;
    				diss=min(min(x1-i+1,i-x2+1),min(y1-j+1,j-y2+1));
    				Add((i-1)*m+j,(x1-1)*m+j,diss);
    				Add((i-1)*m+j,(x2-1)*m+j,diss);
    				Add((i-1)*m+j,(i-1)*m+y1,diss);
    				Add((i-1)*m+j,(i-1)*m+y2,diss);
    			}
    		}
    	}
    	Spfa((qx-1)*m+qy);
    	return 0;
    }
    
    

    T4:柱状图

    模拟退火,(O(1))随机搞最大高度,(O(n))枚举最高点坐标

    口古口古口古

  • 相关阅读:
    Linux _条件变量
    Linux _pthread 线程的同步 浅见
    Linux pthread 线程 浅解
    Linux _守护进程 浅解
    Linux _孤儿进程和僵尸进程 浅见
    Linux 小技巧
    Linux _sem 信号量 V_P
    树莓派ZeroW上传数据文件到阿里云对象存储OSS(Python)
    树莓派ZeroW开机自动运行
    树莓派ZeroW串口通讯
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/13441456.html
Copyright © 2011-2022 走看看