2014 编程之美 资格赛(小数据)

题目1:同构
时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB


给定2个树A和B,保证A的节点个数>=B的节点个数。

现在你需要对树A的边进行二染色。

一个好的染色方案,指不存在一个树A中的连通块,同时满足以下2个条件

1. 其中只有同色的边

2. 和B同构。两个树同构是指,存在一个一一映射(既是单射又是满射),将树B的各节点映射到不同的树A的节点,使得原来在树B中相邻的点,在映射后,仍相邻。

问是否存在一种好的染色方案。

题解:题目的意思刚开始,没看懂,先以我的理解在说一遍题目的意思:就是在A中找出一个连通块,这个连通块和B完全相似(类似相似三角形),且这个连通块只有一种颜色。

        这个时候就有一个疑问的了,如果连通块的边数大于2的话,那可以然不一样的颜色……如果真是这样的,题目那也就太水了!从全局考虑,当你给当前的连通块染成不一样的颜色,很可能这个连通块中的某些边和A其他边重新组成连通块与B相似…

      解题过程:

      1:总共有1<<(n-1)中染色方案,依次枚举染色方案state(只需要找到一种方案不满足条件1,2,就可以不用枚举了,输出答案YES!, state对应的二进制每一位上的值0和1表示数A边的染色情况)

     2:对于每种染色方案,可能有全染色1的边与B同构,全染成0的边与B同构,所以对于每一种染色方案需要对数A搜两次;

     3:关键点是怎么从数A中搜出与数B同构的连通块。我的做法是开一个数组match[]记录B中每个顶点和与A想匹配的顶点,

    一次枚举数B的边,从A中找到一条边(这条边还没和B的其他边匹配且“颜色”和之前选进来的边也要一样)和B的边匹配:

      分情况讨论:
    1)对于数B的边有两个顶点X1,Y1,如果X1与Y1均没有匹配,那么可以和A的边(X2,Y2)中任意一条边匹配(注意划横线的要求), 也有两种匹配方案match[X1]=X2;match[Y1]=Y2和match[Y1]=X2;match[|X1]=Y2;
     2)如果B的边(X1,Y1)有一个顶点,假如X1已经匹配,那么A的边(X2,Y2)必须有一个顶点等于X1才能满足同构料件(使得原来在树B中相邻的点,在映射后,仍相邻);把所有的情况枚举到就OK了!
4:如果一直能匹配到B的最后一条边,说明这种染色方案行不通,修改染色方案!

//source here
#include <stdio.h>
#include <string.h>

#define MAXN  22

int px1[MAXN],py1[MAXN];
int px2[MAXN],py2[MAXN];

int tag[MAXN];
int match[MAXN];

int n,m;
int ans;
int clour;
int cl;


void is_match(int item,int matched){
	                      
	if(item == m-1  || ans == 1 ){
		
		if(matched==m-1)  ans =1 ;
		return ;
	}
	int i;
	for(i=0;i<n-1;i++){  //枚举A的边

		if(tag[i]==1) continue;
		if(cl != ((clour>>i)&1)) continue;  //同种颜色

		
		if(match[px2[item]]==0 && match[py2[item]]==0 ){

			tag[i]=1 ;
			match[px2[item]] = px1[i];
			match[py2[item]] = py1[i];
			
			is_match(item+1,matched+1);
			
			match[px2[item]] = py1[i];
			match[py2[item]] = px1[i];
			
			is_match(item+1,matched+1);
			
			tag[i]= 0 ;
			match[px2[item]] = 0;
			match[py2[item]] = 0;
		}
		
		else if(match[px2[item]]==0 && match[py2[item]]!=0){
			if(py1[i] == match[py2[item]] ){
				match[px2[item]] = px1[i];
				tag[i]=1;
				is_match(item+1,matched+1);
				tag[i]=0;
				match[px2[item]] = 0;	
			}
			if(px1[i] == match[py2[item]] ){
				match[px2[item]] = py1[i];
				tag[i]=1;
				is_match(item+1,matched+1);
				tag[i]=0;
				match[px2[item]] = 0;
			}
		}
		else if(match[px2[item]]!=0 && match[py2[item]]==0){
			if(px1[i] == match[px2[item]] ){
				match[py2[item]] = py1[i];
				tag[i]=1;
				is_match(item+1,matched+1);
				tag[i]=0;
				match[py2[item]] = 0;
				
			}
			if(py1[i] == match[px2[item]] ){
				match[py2[item]] = px1[i];
				tag[i]=1;
				is_match(item+1,matched+1);
				tag[i]=0;
				match[py2[item]] = 0;
				
			}
		}
	}
}
int main(){

	//freopen("data.in","r",stdin);
	int T; scanf("%d",&T);
	int c;
	int i;
	int a,b;
	for(c=1;c<=T;c++){
		
		scanf("%d",&n);
		for(i=0;i<n-1;i++){
			scanf("%d%d",&a,&b);
			px1[i]=a;	py1[i]=b;
		}
		scanf("%d",&m);
		for(i=0;i<m-1;i++){
			scanf("%d%d",&a,&b);
			px2[i]=a;py2[i]=b;
		}
		
		int state= 1<<(n-1);
		
		for(clour=0;clour<state;clour++){
			ans=0;
			memset(match,0,sizeof(match));
			cl=0;is_match(0,0);
			
			if(ans==0){
			    memset(match,0,sizeof(match));
				cl=1;is_match(0,0);
			}
			if(ans == 0)
				break;
		}
		printf("Case %d: %s\n",c,ans==1?"NO":"YES");
	}
	return 0;
}



题目2 : 大神与三位小伙伴



L国是一个有着优美景色且物产丰富的国家,很多人都喜欢来这里旅游并且喜欢带走一些纪念品,大神同学也不例外。距离开L国的时间越来越近了,大神同学正在烦恼给她可爱的小伙伴们带什么纪念品好,现在摆在大神同学面前的有三类纪念品A, B, C可以选择,每类纪念品各有N种。其中种类为A_i, B_i, C_i的纪念品价值均为i, 且分别有N+1-i个剩余。现在大神同学希望在三类纪念品中各挑选一件然后赠送给她的三名可爱的小伙伴,但是她又不希望恰好挑出来两件价值相同的纪念品,因为这样拿到相同价值纪念品的两位小伙伴就会认为大神同学偏袒另一位小伙伴而不理睬她超过一星期。现在,大神同学希望你买到的三件纪念品能让三位小伙伴都开心并且不和她闹别扭,她想知道一共有多少种不同挑选的方法?

因为方案数可能非常大,大神同学希望知道挑选纪念品的方案数模10^9+7之后的答案。

小数据直接暴力求解,按照样例很容易退出计算公式,结果取模的时候每次都判断是不是大于MOD,不然把结果全都累加起来最后取模可能int型会溢出!

#include <stdio.h>

//typedef _int64  ll;

#define MOD   1000000007

int check(int x,int y,int z){
	return  (x==y && x!=z) ||(x==z && x!=y) ||(x!=z && y==z);
}

int main(){

	int T;scanf("%d",&T);
	int c;

	int i,j,k;
	int ans;
	for(c=1;c<=T;c++){
		int n;scanf("%d",&n);
		
		ans =0;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			{
				for(k=1;k<=n;k++){
					if(check(i,j,k)) continue;
					
					ans+=(n+1-i)*(n+1-j)*(n+1-k);
					if(ans>=MOD) //减法比取模的效率大得多,所以能用减法代替就用减法代替
						ans-=MOD; 
				}
			}
		}
		printf("Case %d: %d\n",c,ans);
	}
	return 0;
}


题目3 : 格格取数

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB


给你一个m x n (1 <= m, n <= 100)的矩阵A (0<=aij<=10000),要求在矩阵中选择一些数,要求每一行,每一列都至少选到了一个数,使得选出的数的和尽量的小。


输入

多组测试数据。首先是数据组数T

对于每组测试数据,第1行是两个正整数m, n,分别表示矩阵的行数和列数。

接下来的m行,每行n个整数,之间用一个空格分隔,表示矩阵A的元素。


题解:刚开始还以为是八皇后之类的问题,后来学长提示才发现和八皇后没有一毛钱关系。

 给一组样例:

 3   3

 1    1   1

 1  100 100

 1  100  100

正确答案是:4

题解:深度搜索和二进制表示状态,数据很小,很容易通过!

#include <stdio.h>
#include <string.h>

#define  MIN(a,b) (a)>(b)?(b):(a)

#define   MAXN   6

int map[MAXN][MAXN];
int minV[MAXN];             //每一行里面的最小值
int Sum[MAXN][1<<MAXN];
int n,m,res,state;
int getValue(int r,int state){

	int ans=0;
	int i;
	for( i=0;i<n;i++){
		if(state&(1<<i))
		    ans+=map[r][i];
	}
	return ans;
}

void dfs(int r,int st,int sum){
	if(r == m-1  || sum>=res){
		if(state ==st) 
			res=MIN(res,sum);
		return;
	}
	if(st == state){ //一个小小的优化
		dfs(r+1,st,sum+minV[r+1]);
	}
	else{
		int i;
		for(i=1;i<state;i++)
			dfs(r+1,st|i,sum+Sum[r+1][i]);
	}
}
int main(){

	//freopen("data.in","r",stdin);

	int T;scanf("%d",&T);
	int c;
	for(c=1;c<=T;c++){
		scanf("%d%d",&m,&n);

		memset(map,0,sizeof(map));
		memset(Sum,0,sizeof(Sum));
		int i,j;
		for(i=0;i<m;i++){
			minV[i]=0x7fffffff;
			for(j=0;j<n;j++)
			{
				scanf("%d",&map[i][j]);
				minV[i]=MIN(minV[i],map[i][j]);
			}
		}
		state=(1<<n)-1;
		for(i=0;i<m;i++)
			for(j=1;j<=state;j++)
				Sum[i][j]=getValue(i,j);
		res=0x7ffffff;
		for(i=1;i<=state;i++){
			dfs(0,i,Sum[0][i]);
		}
		printf("Case %d: %d\n",c,res);
	}
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值