crazy Rows 2009 Round2 A

/*crazy Rows 2009 Round2 A
给定一个有0 1 组成的矩阵,只允许交换相邻的两行
要把矩阵化为下三角矩阵(主对角线上方的元素都是0)
最少需要交换几次?
输入的矩阵保证总能化成下三角矩阵
限制:N∈[1,100]
time <=1S
内存 <=65535KB
EG:
1 1 1 0         1 1 1 0                 1 0 0 0
1 1 0 0  ____>  1 1 0 0  _____>···· 1 1 0 0
1 1 0 0      >  1 0 0 0       >···· 1 1 0 0
1 0 0 0         1 1 0 0                 1 1 1 0

  
	输入3
	001
	100
	010
	输出 2   (交换1 2 再交换2 3 )
	
	  解:
	  先说内存 65535KB->64MB  若用int有点浪费空间 不如用char存储矩阵
	  如果真的一个一个尝试 那么就有N!种 显然时间上不允许
	  
		所以我们先暂时考虑: 最后应该把哪一行交换到第一行,第一行应该为全0行或首元素为1其他为0的行
		而这一行可以交换到任意一行 当有多个满足条件的行时选择离第一行近的行对应的最终费用小
		因为只能交换相邻的两行
		假设:X1 X2 X3……Xn   其中X1的元素不应该在第一个位置
		而这n个元素只有 X2 X3的元素 可以在X1的位置
		那么若交换 X2 X1则需要一步 然后X3又和X1交换  共两步 得到 X2 X3 X1……
		若把X3交换到第一行    两步 X2再与X1交换一步  共三步 得到 X3 X2 X1……
		这两种情况来看 第二种可以看作是第一种移动完了之后 又交换了 X2 X3
		如果X1 X2 X3之间出现了多个不应该在第一个的元素 是一样的道理 
		你可能会想 在X1 X2 X3之间插入的元素 万一出现也适合第一个位置的怎么办 
		其实上述的X1 X2 X3 是有条件的筛选的  每两个之间没有可以当第一个位置的元素
		如果有 比如 X1 Xm X2 X3中 Xm也符合第一个位置
		那么 此时应该把 X1 Xm X2 分别当作上述情况的 X1 X2 X3 她们只是代号而已
		
		  这个地方懂了 也就明白 为什么当有多个满足条件的行时选择离第一行近的行交换 则对应的最终费用小了。
		  确定第一行之后就没必要再动它 对于之后的行可以进行相同的处理
		  每一行中的0 1 数量多少并不重要
		  重要的是 最后一个1的位置
		  若将最后的1的位置记录下来 有利于降低复杂度
		  代码:
*/
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# define MAX 60
int Swep(int *Flagi,int *Flagj);//交换函数 返回1
int main(){
	char JZ[MAX][MAX+1]={"\0"};//将字符数组初始化为0
	int i,j,N,Flag[MAX]={0},sum=0,TU;//Flagi记录第i行的最后一个1出现的位置
	/*****这两行之间的都是生成随即矩阵的 只需要输入行数就能得到随机的符合条件的矩阵********/
	char MM[MAX+2]={"\0"},SS,QQ[MAX];//这个地方和程序没关系 只是为了生成随机矩阵而设的变量
    srand(time(NULL));
	for(i=0;i<MAX;i++)
		MM[i]=i+1;//生成矩阵行
	printf("输入N[1,%d]:",MAX);
	scanf("%d",&N);//输入矩阵的行数
	SS=rand()%N;
    for(i=0;i<N;i++)
	{
		while(!MM[SS])
			SS=rand()%N;
		QQ[i]=SS;
		MM[SS]=0;
	}
    for(i=0;i<N;i++)
		for(j=0;j<=QQ[i];j++)
			JZ[i][j]=rand()%4>=1;//生成随机矩阵为了提高1的比重(rand()%6>1)
		printf("随机矩阵:\n");
		for(i=0;i<N;i++)
		{
			for(j=0;j<N;j++)
				printf("%d",JZ[i][j]);
			printf("\n");
		}
		/*****这两行之间的都是生成随即矩阵的 只需要输入行数就能得到随机的符合条件的矩阵********/
		for(i=0;i<N;i++)
		{
			Flag[i]=-1;//每一行先假设没有1的情况下Flagi为-1
			for(j=0;j<N;j++)
				if(JZ[i][j]) Flag[i]=j;//这样就记录完每行末尾1的位置
		}
		for(i=0;i<N;i++)
		{
			TU=-1;//TU记录要移动到第i行的行
			for(j=i;j<N;j++)
				if(Flag[j]<=i)//一旦寻找到离i最近的行
				{
					TU=j;   //记录 并执行交换
					break;
				}
				for(j=TU;j>i;j--)//交换
				{
					sum+=Swep(&Flag[j],&Flag[j-1]);
					printf("交换%02d  %02d两行\n",j,j+1);//为了快速得到最终结果 可删掉这一行
				}
		}
		printf("至少需要移动%d次\n",sum);
		return 0;
}
int Swep(int *Flagi,int *Flagj)
{
	int temp=*Flagi;
	*Flagi=*Flagj;
	*Flagj=temp;
	return 1;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值