SPOJ 196 Musketeers(JAVA版)

SPOJ 196 Musketeers

題目大意:有n个人要进行n-1场决斗,最后只能有一个人存活,决斗可以从这n个人任意一个开始,但是只有相邻的两个人能进行决斗,问最后哪些人有可能活下来,输出人数,并把他们的序号从小到大输出。

思路如下:因为这n个人排成一个圈,任意相邻两个人可以进行决斗(最后一个人可以和第一个人决斗,一个圈),于是先用n+1长度的数组构造一个“人链”,数组的开头和结尾是同一个人比如:有5个人,先从第一个人开始看,1站排头,即a0,a1,a2,a3,a4,a5分别是1,2,3,4,5,1。每有一个人死亡,那个人就退场,直到最后,如果能够首尾相邻那就表示那个人能够获胜。考虑获胜前最后一个状态便可。即第k个人,可以被排头的人打败或者被排尾的人打败,并且最后会剩下三个人(排头,k,排尾)。如此排头(即排尾)的这个人就能获胜,这就知道是dp了。
dp[i][j]表示第 i 人与第 j 人能否相遇,能为1,否为0;a[i]表示人链中排第 i 的人;f[i][j]用来记录题目中的数组。

动态转移方程:dp[i][j]=(((f[a[i]][a[k]] || f[a[j]][a[k]]) && dp[i][k] && dp[k][j])?1:0);(i<k<j)

这里附上两份代码,因为第一次做是用递归(记忆化搜索)做的,后来想想,因为习惯,改成了循环。

AC代码:(循环)

import java.util.Scanner;

public class Main
{
	static Scanner scan=new Scanner(System.in);
	public static void main(String[] args)
	{
		int t=scan.nextInt();
		while(t-->0)
		{
			int n=scan.nextInt();
			int f[][]=new int[n+1][n+1];
			String str=scan.nextLine();
			
			for(int i=1;i<n+1;i++)
			{
				str=scan.nextLine();
				for(int j=1;j<n+1;j++)
					f[i][j]=str.charAt(j-1)-48;
			}
			
			int dp[][]=new int[n+1][n+1];
			int a[]=new int[n+1];
			int ans[]=new int[n+1];//这个数组用来记录获胜的人
			int cas=0;
			
			for(int len=1;len<n+1;len++)//len表示第len个人在排头(考察他是否能胜出)
			{
				int meet=len;
				for(int k=0;k<n+1;k++)//这个循环构造人链
				{
					if(meet<=n)
						a[k]=meet;
					else
						a[k]=meet-n;
					meet++;
				}
				for(int i=1;i<n+1;i++)
					for(int j=0;j+i<n+1;j++)
					{
						if(j+1==j+i)//如果左右相邻,直接就是能两人能相遇
							dp[j][j+i]=1;
						else
						{	
							for(int k=j+1;k<j+i;k++)//找人
								if((f[a[j]][a[k]]==1 || f[a[j+i]][a[k]]==1) && dp[j][k]==1 && dp[k][j+i]==1)
									{dp[j][j+i]=1;break;}//满足条件就赋1,然后break
								else dp[j][j+i]=0;//因为这个数组要用很多次,所以找不到还是赋0
						}
					}
				if(dp[0][n]==1)//相遇了
					ans[cas++]=len;//记
			}
			System.out.println(cas);
			for(int i=0;i<cas;i++)
				System.out.println(ans[i]);
		}
	}
}
AC代码:(递归)

import java.util.Scanner;

public class Main//标注的地方是跟循环不一样的地方
{
	static Scanner scan=new Scanner(System.in);
	public static int winner(int L, int R, int[] a, int[][] dp, int[][] f)
	{
		if(dp[L][R]!=-1)//记忆化搜索,看看是否检查过
			return dp[L][R];
		if(L+1==R)
			return dp[L][R]=1;
		int flag=0;
		for(int i=L+1;i<R;i++)
			if((f[a[L]][a[i]]==1 || f[a[R]][a[i]]==1) && winner(L, i, a, dp, f)==1 && winner(i, R, a, dp, f)==1)
			{	
				flag=1;
				break;
			}
		return dp[L][R]=flag;
	}
	
	public static void main(String[] args)
	{
		int t=scan.nextInt();
		while(t-->0)
		{
			int n=scan.nextInt();
			int f[][]=new int[n+1][n+1];
			String str=scan.nextLine();

			for(int i=1;i<n+1;i++)
			{
				str=scan.nextLine();
				for(int j=1;j<n+1;j++)
					f[i][j]=str.charAt(j-1)-48;
			}
			
			int dp[][]=new int[n+1][n+1];
			int a[]=new int[n+1];
			int ans[]=new int[n+1];
			int cas=0;
			
			for(int i=1;i<n+1;i++)
			{
				for(int x=0;x<n+1;x++)
					for(int y=0;y<n+1;y++)
						dp[x][y]=-1;//初始化,每进一次循环初始化一次,全部为-1,因为未检查(这个地方在记忆化搜索起作用)
				
				int meet=i;//构造人链
				for(int j=0;j<n+1;j++)
				{
					if(meet<=n)
						a[j]=meet;
					else
						a[j]=meet-n;
					meet++;
				}
				if(winner(0, n, a, dp, f)==1)//0与n能否相遇,即首尾
					ans[cas++]=i;
			}
			System.out.println(cas);
			for(int i=0;i<cas;i++)
				System.out.println(ans[i]);
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值