USACO Section 2.1

城堡 The Castle

爆肝三个小时祭.

对于第一问其实就是求联通块的个数,第二问求最大联通块所包含的节点,这个可以一个DFS一起求.具体来说我们先存图,设(b[i][j][0/1/2/3])表示点$(i,j)$4面是否有墙,然后每次从一个未访问过的点开始枚举k=0/1/2/3然后向外拓展,同时记录该点属于第几个联通块(第4问有用),该联通块包含节点个数等信息.

第三问和第四问是一起的.我们直接枚举把哪个墙推掉可以得到最大联通块,因为我们上面DFS记录了每个联通块的节点个数和每个点属于第几个联通块,所以我们枚举的时候只要判断墙两边的节点是否属于不同的联通块,如果是就可以把两个联通块的节点个数相加与maxn比较大小.

对于第四问,因为是输出方案,且西边最优,南边次优,北墙比东墙优,所以我们先顺序枚举列(保证最西),然后倒序枚举行(保证最南),运用用第三问相同的方法求推掉墙后的新联通块个数是否等于第三问所求即可.如果是直接输出(此时一定是符合题意的最优墙).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=55;
int n,m,now,ans1,ans2,ans3;
int visit[N][N],size[N*N];
int a[N][N],b[N][N][4];
inline void dfs(int x,int y){
	if(x<1||x>n||y<1||y>m)return;
	visit[x][y]=ans1;++now;++size[ans1];
	for(int i=0;i<=3;++i){
		if(b[x][y][i]==1)continue;
		if(i==0&&!visit[x][y-1])dfs(x,y-1);
		if(i==1&&!visit[x-1][y])dfs(x-1,y);
		if(i==2&&!visit[x][y+1])dfs(x,y+1);
		if(i==3&&!visit[x+1][y])dfs(x+1,y);
	}
	return;
}
int main(){
	m=read(),n=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			a[i][j]=read();
			for(int k=0;k<=3;++k){
				if(a[i][j]&(1<<k))b[i][j][k]=1;
			}
		}
	memset(size,0,sizeof(size));
	while(now<n*m){
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				if(visit[i][j]==0){
					++ans1;dfs(i,j);
					ans2=max(ans2,size[ans1]);
				}
	}
	printf("%d
%d
",ans1,ans2);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			for(int k=0;k<=3;++k){
				if(b[i][j][k]){
					if(k==0){
						if(j-1<1)continue;
						if(visit[i][j]==visit[i][j-1])continue;
						ans3=max(ans3,size[visit[i][j]]+size[visit[i][j-1]]);
					}
					if(k==1){
						if(i-1<1)continue;
						if(visit[i][j]==visit[i-1][j])continue;
						ans3=max(ans3,size[visit[i][j]]+size[visit[i-1][j]]);
					}
					if(k==2){
						if(j+1>m)continue;
						if(visit[i][j]==visit[i][j+1])continue;
						ans3=max(ans3,size[visit[i][j]]+size[visit[i][j+1]]);
					}
					if(k==3){
						if(i+1>n)continue;
						if(visit[i][j]==visit[i+1][j])continue;
						ans3=max(ans3,size[visit[i][j]]+size[visit[i+1][j]]);
					}
				}
			}
		}
	}
	printf("%d
",ans3);
	for(int j=1;j<=m;++j)
		for(int i=n;i>=1;--i){
			if(b[i][j][1]&&i-1>=1){
				if(visit[i][j]!=visit[i-1][j])
					if(size[visit[i][j]]+size[visit[i-1][j]]==ans3){
						printf("%d %d N
",i,j);
						return 0;
					}
			}
			if(b[i][j][3]&&i+1<=n){
				if(visit[i][j]!=visit[i+1][j])
					if(size[visit[i][j]]+size[visit[i+1][j]]==ans3){
						printf("%d %d N
",i+1,j);
						return 0;
					}
			}
			if(b[i][j][0]&&j-1>=1){
				if(visit[i][j]!=visit[i][j-1])
					if(size[visit[i][j]]+size[visit[i][j-1]]==ans3){
						printf("%d %d E
",i,j-1);
						return 0;
					}
			}
			if(b[i][j][2]&&j+1<=m){
				if(visit[i][j]!=visit[i][j+1])
					if(size[visit[i][j]]+size[visit[i][j+1]]==ans3){
						printf("%d %d E
",i,j);
						return 0;
					}
			}
		}
    return 0;
}

顺序的分数 Ordered Fractions

直接枚举出所有的分数,然后sort排序.但是要注意枚举的时候一些细节.比如分母不为零,gcd(分子,分母)=1,分子为零时唯一合法答案是"0/1"等等.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
struct ppx{
	double val;int i,j;
}a[40005];
inline bool cmp(const ppx &x,const ppx &y){return x.val<y.val;}
inline int gcd(int a,int b){
	if(b==0)return a;
    return gcd(b,a%b);
}
int main(){
	int n=read(),tot=0;
	for(int i=0;i<=n-1;++i){
		for(int j=i+1;j<=n;++j){
			if(i==0&&j==1){
				a[++tot].val=(double)i*1.0/j;
				a[tot].i=i;a[tot].j=j;
				continue;
			}
			if(i==1){
				a[++tot].val=(double)i*1.0/j;
				a[tot].i=i;a[tot].j=j;
				continue;
			}
			if(i!=0){
				if(gcd(i,j)==1){
					a[++tot].val=(double)i*1.0/j;
					a[tot].i=i;a[tot].j=j;
					continue;
				}
			}
		}
	}
	sort(a+1,a+tot+1,cmp);
	for(int i=1;i<=tot;++i)
		printf("%d/%d
",a[i].i,a[i].j);
	printf("1/1
");
    return 0;
}

三值的排序 Sorting a Three-Valued Sequence

一开始还以为是归并排序求逆序对的模板题,但本题可以不是相邻两个数交换...

贪心地想,先把a数组从小到大排序存到b数组,然后比较a,b数组的相应位置i如果不同则优先找交换该次两个位置i,j都合法的j交换(即不仅a[j]=b[i]而且a[i]=b[j]),如果没有再选择仅满足a[j]=b[i]的j.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1005;
int a[N],b[N];
int main(){
	int n=read();
	for(int i=1;i<=n;++i)a[i]=read(),b[i]=a[i];
	sort(b+1,b+n+1);
	int ans=0;
	for(int i=1;i<=n;++i){
		if(a[i]!=b[i]){
			++ans;int bj=0;
			for(int j=n;j>=i+1;--j){
				if(a[j]==b[i]&&a[i]==b[j]){bj=1;swap(a[i],a[j]);break;}
			}
			if(bj)continue;
			for(int j=n;j>=i+1;--j){
				if(a[j]==b[i]){bj=1;swap(a[i],a[j]);break;}
			}
		}
	}
	printf("%d
",ans);
    return 0;
}

健康的荷斯坦奶牛 Healthy Holsteins

直接DFS会超时,考虑剪枝.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=50;
int n,m,a[N][N];
int v[N],b[N],visit[N];
inline bool check(){
	for(int i=1;i<=n;++i)
		if(b[i]<v[i])return false;
	return true;
}
inline void dfs(int now,int nowm,int goal){
	if(now>goal){
		if(check()){
			printf("%d ",goal);
			for(int i=1;i<=m;++i)
				if(visit[i])printf("%d ",i);
			printf("
");
			exit(0);
		}
		return;
	}
	if(m-nowm+1<goal-now)return;//剪枝1
	for(int i=nowm;i<=m;++i){//剪枝2:从上一次选的位置+1开始选
		if(!visit[i]){
			visit[i]=1;
			for(int j=1;j<=n;++j)b[j]+=a[i][j];
			dfs(now+1,i+1,goal);
			visit[i]=0;
			for(int j=1;j<=n;++j)b[j]-=a[i][j];
		}
	}
}
int main(){
	n=read();for(int i=1;i<=n;++i)v[i]=read();
	m=read();
	for(int i=1;i<=m;++i)
		for(int j=1;j<=n;++j)
			a[i][j]=read();
	for(int i=1;i<=m;++i){
		for(int j=1;j<=m-i+1;++j){
			memset(visit,0,sizeof(visit));
			memset(b,0,sizeof(b));
			dfs(1,j,i);
		}
	}
    return 0;
}

海明码 Hamming Codes

本题的难度在于看懂题意???一直不晓得B是干嘛的.

直接从小到大枚举0到(2^{B+1}-1),然后判断是否跟之前的数距离大于等于D即可,输出前面n个即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
int n,m,B,D,ans[105];
inline bool pd(int x,int y){
    int s=x^y,sum=0;
    for(int i=0;(1<<i)<=s;i++)
        if((1<<i)&s)sum++;
    return sum>=D;
}
inline bool check(int x){
    for(int i=1;i<=m;i++)
        if(!pd(x,ans[i]))return false;
    return true;
}
int main(){
    n=read(),B=read(),D=read();
    for(int i=0;i<(1<<(B+1))&&m<n;++i)
        if(check(i))ans[++m]=i;
    for(int i=1;i<=n;++i){
        printf("%d ",ans[i]);
        if(i%10==0)puts("");
    }puts("");
    return 0;
}
原文地址:https://www.cnblogs.com/PPXppx/p/11298852.html