笔记

Oil Deposits

题目大意

给定一个矩形区域,询问有多少个全为‘@’的连通块。

a与b属于同一连通块当且仅当至少满足下列的一个条件:
1,a与b相邻。(当a在以b为中心的8个位置中的一个时,认为a与b相邻)
2,a的相邻点与b或b的相邻点属于同一连通块。
3,b的相邻点与a或a的相邻点属于同一连通块。

数据输入

输入可能有多个矩形区域(即可能有多组测试)。
每个矩形区域的起始行包含m和n,表示行和列的数量,1<=n,m<=100。
当m =0时,输入结束。
接下来是n行,每行m个字符。
每个字符对应一个小方格,要么是’*’,代表禁止区域,要么是’@’。

数据输出

对于每一个矩形区域,输出’@'的连通块数量。

样例

输入

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5 
****@
*@@*@
*@**@
@@@*@
@@**@
0 0 

输出

0
1
2
2

算法思路

深度优先搜索加标记

八个方向分别搜索,然后读取的时候需要按行读取,外围加上一圈‘*’不用判断越界问题

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
int n,m;
char c[105][105];
int f[105][105];
int tot=0;
void dfs(int x,int y,int flag){
	f[x][y]=flag;
	if(c[x+1][y]=='@'&&f[x+1][y]==0){
		dfs(x+1,y,flag);//往下走 
	}
	if(c[x][y+1]=='@'&&f[x][y+1]==0){
		dfs(x,y+1,flag);//往右走 
	}
	if(c[x-1][y]=='@'&&f[x-1][y]==0){
		dfs(x-1,y,flag);//往上走 
	}
	if(c[x][y-1]=='@'&&f[x][y-1]==0){
		dfs(x,y-1,flag);//往左走 
	}
	if(c[x+1][y+1]=='@'&&f[x+1][y+1]==0){
		dfs(x+1,y+1,flag);//往右下走 
	}
	if(c[x+1][y-1]=='@'&&f[x+1][y-1]==0){
		dfs(x+1,y-1,flag);//往左下走 
	}
	if(c[x-1][y-1]=='@'&&f[x-1][y-1]==0){
		dfs(x-1,y-1,flag);//往左上走 
	}
	if(c[x-1][y+1]=='@'&&f[x-1][y+1]==0){
		dfs(x-1,y+1,flag);//往右上走 
	}
}
int main(){
	while(1){
		scanf("%d%d",&n,&m);
		if(n==0&&m==0){
			break;
		}
		string s;
		for(int i=1;i<=n;i++){
			cin>>s;
			for(int j=1;j<=m;j++){
				c[i][j]=s[j-1];
				c[0][j]='*';
				c[n+1][j]='*';
			}
			c[i][0]='*';
			c[i][m+1]='*';
		}
		c[0][0]=c[n+1][m+1]=c[0][m+1]=c[n+1][0]='*';
		tot=0;
		for(int i=0;i<=n+1;i++){
			for(int j=0;j<=m+1;j++){
				f[i][j]=0;
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(c[i][j]=='@'&&f[i][j]==0){
					tot++;
					dfs(i,j,tot);
				}
			}
		}
		cout<<tot<<endl;
	}
	return 0;
}

蜘蛛牌

题目介绍

蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起移动,游戏的目的是将所有的牌按同一花色从小到大排好,为了简单起见,我们的游戏只有同一花色的10张牌,从A到10,且随机的在一行上展开,编号从1到10,把第i号上的牌移到第j号牌上,移动距离为abs(i-j),现在你要做的是求出完成游戏的最小移动距离。

数据输入

第一个输入数据是T,表示数据的组数。
每组数据有一行,10个输入数据,数据的范围是[1,10],分别表示A到10,我们保证每组数据都是合法的。

数据输出

对应每组数据输出最小移动距离。

样例

输入

1
1 2 3 4 5 6 7 8 9 10

输出

9

算法思路

深搜加剪枝

深搜枚举所有情况,找到最小的那种

通过剪枝操作减少搜索次数,提高运算效率

存储每张牌的位置然后按照牌的大小进行深搜

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int t;
int a[15];
bool f[15];
int ans;
int Abs(int x){
	return x>0?x:-x;
}
void dfs(int num,int sum){
	if(sum>=ans)return ;
	if(num==9){
		ans=sum;
		return ;
	}
	for(int i=1;i<10;i++){
		if (f[i]==0)
		{
			f[i]=1;
			for(int j=i+1;j<=10;j++){
				if (f[j]==0)
				{
					dfs(num+1,sum+Abs(a[i]-a[j]));
					break;
				}
			}
			f[i]=0;
		}
		
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		for(int i=1;i<=10;i++){
			int x;
			scanf("%d",x);
			a[x]=i;
		}
		memset(f,0,sizeof(f));
		ans=1e5+7;
		dfs(0,0);
		cout<<ans<<endl;
	}
	return 0;
}

N皇后 八皇后

题目介绍

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

数据输入

共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。

数据输出

共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

样例

输入

1
8
5
0

输出

1
92
10

算法思路

深搜+剪枝+打表+找规律

问题分析

1.一行只能放一个皇后,所以我们一旦确定此处可以放皇后,那么该行就只能放一个皇后,下面的就不要再搜了。

2.每一列只能放一个皇后,所以我们下次搜索就不要再搜已经放过的皇后了。

3.斜的45°线也只能放一个。

重点是45°的情况不好判断

左上到右下左下到右上
0 1 2 3 40 1 2 3 4
-1 0 1 2 31 2 3 4 5
-2 -1 0 1 22 3 4 5 6
-3 -2 -1 0 13 4 5 6 7
-4 -3 -2 -1 04 5 6 7 8
列-行相等行+列相等

因为N<=10所以我们只需要先跑出所有情况然后 直接输入一次判断一次判断就好了

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
int n,sum;
int f[3][50],ans[15];
void dfs(int now,int N){
	if(now==N+1){
		sum++;
		return ;
	}
	for(int i=1;i<=N;i++){
		if(f[0][now-i+N]==0&&f[1][i]==0&&f[2][now+i]==0){//f[0]判断下对角线,f[1]判断列,f[2]判断上对角线
			f[0][now-i+N]=1;
			f[1][i]=1;
			f[2][now+i]=1;
			dfs(now+1,N);
			f[0][now-i+N]=0;
			f[1][i]=0;
			f[2][now+i]=0;
		}
	}
}
int main(){
	for(int i=1;i<=10;i++){
		memset(f,0,sizeof(f));
		sum=0;
		dfs(1,i);
		ans[i]=sum;
	}
	while(cin>>n&&n){
		cout<<ans[n]<<endl;
	}
	return 0;
}

Agri-Net

题目大意

有n个农场,已知这n个农场都互相相通,有一定的距离,现在每个农场需要装光纤,问怎么安装光纤能将所有农场都连通起来,并且要使光纤距离最小,输出安装光纤的总距离
任意两个村庄之间的距离小于 100,000.

数据输入

输入包含多组数据。对于每组数据, 第一行包含一个整数N表示农场的数量 (3 <= N <= 100). 接下来是一个
N ∗ N N*N NN
的邻接矩阵,表示各个村庄之间的距离 . 当然,对角线将是0,因为从农场到农场的距离对于这个问题并不有趣。

数据输出

对于每种情况,请输出一个整数表示长度,该长度是连接整个农场集合所需的最小光纤长度的总和。

样例

输入

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0

输出

28

题目思路

最小生成树 克鲁斯卡尔和prim

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n;
struct edge{
	int u,v,w;
};
vector<edge>e;
int fa[105];
int ans=0;
bool cmp(edge a,edge b){
	return a.w<b.w;
}
int find(int x){
	if(fa[x]==x)
        return x;
    else
        return fa[x]=find(fa[x]);
}
void uunion(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y){
		return ;
	}
	else{
		fa[y]=x;
	}
}
int main(){
	while(scanf("%d",&n)!=EOF){
		int tot=0;
		e.clear();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				edge k;
				cin>>k.w;
				if(i!=j){
					k.u=i;
					k.v=j;
					e.push_back(k);
				}
			}
		}
		sort(e.begin(),e.end(),cmp);
		for(int i=0;i<=n;i++){
			fa[i]=i;
		}
		int cnt=0;
		ans=0;
		for(int i=0;i<e.size();i++){
			if(find(e[i].u)!=find(e[i].v)){
				uunion(e[i].u,e[i].v);
				ans+=e[i].w;
				cnt++;
			}
		}
		if(cnt==n-1)
	    	printf("%d\n",ans);
		else
			printf("-1\n");
	}
	return 0;
}

Jungle Roads

题目大意

小镇的街道非常杂乱,使得道路的维护费用十分昂贵。 所以我们不得不拆除某些道路。 现在已知重建前,各个店铺之间道路的维护费用。 作为小镇的管理者,你想知道将所有店铺连接起来,需要花费最少的金额。

数据输入

(最多100次询问)对于每次询问: 第一行给出店铺数量n; 接下来第二行到第n+1行, 给出当前店铺名称a ,与之直接相连的店铺的道路数k ,之后k组数据表示 与a相连的店铺名称和他们之间的道路维护费用 (多组数据输入,当店铺数量为0时,输入结束) 1 < n < 27, 0 <= k <= 15

数据输出

重建后需要花费的最少金额 上一个题我们是直接得到了一个完整的邻接矩阵,也就是说 我们可以通过邻接矩阵直接获取两点之间的边权

给你一个点 然后 这个点到另外的点的距离 也就是说 这是给你了一个图需要你自己去存储

样例

输入

9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0

输出

216
30

算法思路

邻接表+克鲁斯卡尔最小生成树

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

int n;
int fa[30];
int ans=0;
struct edge{
	int u,v,w;
};
edge e[900];
int cnt=0;
void add_edge(int u,int v,int w){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
}
bool cmp(edge a,edge b){
	return a.w<b.w;
}
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]);
}
void uunion(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y){
		return ;
	}
	else{
		fa[y]=x;
	}
}
int main(){
	while(cin>>n&&n){
		for(int i=1;i<=26;i++){
			fa[i]=i;
		}
		cnt=0;
		for(int i=1;i<n;i++){
			char x;
			int s;
			cin>>x>>s;
			for(int j=1;j<=s;j++){
				char v;
				int w;
				cin>>v>>w;
				add_edge(x-'A'+1,v-'A'+1,w);
			}
		}
		sort(e+1,e+1+cnt,cmp);
		ans=0;
		for(int i=1;i<=cnt;i++){
			int u=e[i].u;
			int v=e[i].v;
			int w=e[i].w;
			if(find(fa[u])!=find(fa[v])){
				uunion(u,v);
				ans+=w;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

Truck History

题目大意

编写程序输出最高质量的火车推导计划。火车推倒计划质量的求解公式中的分子为1,分母为t0,td的距离总和。

t0,td的距离就是两个输入字符串的字母不同位置的个数。想要推导计划的质量最高就是使分母最小。即t0,td的距离总和最小。

数据输入

多组输入,以0结束。 保证N不超过2000。

数据输出

每组输出"The highest possible quality is 1/Q."。

样例

输入

4
aaaaaaa
baaaaaa
abaaaaa
aabaaaa
0

输出

The highest possible quality is 1/3.

算法思路

字符串比对+克鲁斯卡尔最小生成树+常数优化 KMP 字符串比对算法

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>

using namespace std;
const int MAXN=2000+2;
struct edge{
	int u,v,w;
};
edge e[MAXN*MAXN];
string s[MAXN];
int cnt=0;
int ans=0;
int fa[MAXN];
int n;
bool cmp(edge a,edge b){
	return a.w<b.w;
}
void init(){
	cnt=0;
	ans=0;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
}
void make_edge(){
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			int sum=0;
			int len=s[i].length();
			for(int l=0;l<len;l++){
				if(s[i][l]!=s[j][l])
					sum++;
			}
			cnt++;
			e[cnt].u=i;
			e[cnt].v=j;
			e[cnt].w=sum;
		}
	}
}
inline int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void kru(){
	for(int i=1;i<=cnt;i++){
		int x=find(e[i].u);
		int y=find(e[i].v);
		int w=e[i].w;
		if(x!=y){
			fa[y]=x;
			ans+=w;
		}
	}
	printf("The highest possible quality is 1/%d.\n",ans);
}
int main(){
	while(~scanf("%d",&n)&&n){
		for(int i=1;i<=n;i++){
			std::cin>>s[i]; 
		}
		init();
		make_edge();
		sort(e+1,e+1+cnt,cmp);
		kru();
	}
	return 0;
}

Connections between cities

题目大意

一场突如其来的爆炸,摧毁了许多村子之间的通道。爆炸后,我们得知有些村子之间不能直接或间接相通了,但是也是不存在圈的,就是说不存在这样一条路,从某个村子出发通过一些村子又回到了最初的村子。
我们希望你们写一个程序帮我们确定两个村子之间的最短路径,或者确定他们不能相通。

数据输入

输入由多个问题实例组成。对于每个实例,第一行包含三个整数n、m和c, 2<=n<=10000, 0<=m<10000, 1<=c<=1000000。n代表村子编号的数量从1到n。m行,每一行有三个整数i, j和k,代表一个村子i和j之间的道路,与长度k。c代表询问的次数,接下来每行两个整数i, j,表示我们想知道村子i和j之间是否相通,相通的话最短路径是多少。

数据输出

对于每个问题实例,每个查询对应一行。如果两城市之间没有路径,输出“Not connected”,否则输出两城市之间最短路径的长度

样例

输入

5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5

输出

Not connected
6

算法思路

建树+倍增+LCA+树节点路径长度 求 最小生成树 LCA 倍增LCA 树节点路径长度 树的直径

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 1e5+5;
const int ins = 20;
int f[maxn][ins];
int pre[maxn];
int dis[maxn];
int dep[maxn];
struct node
{
	int v,next,w;
}e[maxn<<1];
int head[maxn],cnt;
int n,m,q;
void add(int u,int v,int w)
{
	e[++cnt].v = v;
	e[cnt].w = w;
	e[cnt].next = head[u];
	head[u] = cnt;
}
void init()
{
	cnt = 0;
    for(int i = 0; i <= n; i++)
		pre[i] = i;
    memset(head , -1, sizeof(head));
    memset(f, 0, sizeof(f));
    memset(dis, 0, sizeof(dis));
    memset(dep, 0, sizeof(dep));
}
int findd(int x)
{
	return x == pre[x] ? x : pre[x] = findd(pre[x]);
}
void merage(int a,int b)
{
	a = findd(a);
	b = findd(b);
    if(a != b)
        pre[a] = b;
}
void dfs(int x,int p,int d)
{
	f[x][0] = p;
	dep[x] = dep[p] + 1;
	dis[x] = d;
	for(int i=1;i<=19;i++)
	{
		f[x][i] = f[f[x][i - 1]][i - 1];
	}
	for(int i=head[x];~i;i=e[i].next)
	{
		int v = e[i].v;
		if(v != p)
		{
			dfs(v, x, dis[x] + e[i].w);
		}
	}
}
int lca(int a,int b)
{
	if(dep[a] > dep[b])
	swap(a,b);
	for(int i=19;i>=0;i--)
	{
		if(dep[f[b][i]] >= dep[a])
		b = f[b][i];
	}
	if(a == b)
	return a;
	for(int i=19;i>=0;i--)
	{
		if(f[a][i] != f[b][i])
		{
			a = f[a][i];
			b = f[b][i];
		}
	}
	return f[a][0];
}
int main()
{
	while(scanf("%d%d%d",&n,&m,&q) == 3)
	{
		int u,v,w;
		init();
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,w);
			add(v,u,w);
			merage(u,v);
		}
		for(int i=1;i<=n;i++)
		{
			if(pre[i] == i)
			dfs(i, 0, 0);
		}
		while(q--)
		{
			scanf("%d%d",&u,&v);
			if(findd(u) == findd(v))
			printf("%d\n",dis[u] + dis[v] - 2 * dis[lca(u, v)]);
			else
			printf("Not connected\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值