拯救亚特兰蒂斯题解

拯救亚特兰蒂斯(savior.pas)

Bug魔王派出了N种怪物来入侵亚特兰蒂斯,聪明的亚特兰蒂斯国王Cubic预先得知了Bug魔王的阴谋,并了解到每种怪物都对应一种剑术和一种法术,这种怪物可以被对应的剑术打败也可以被对应的法术打败。不同的怪物可能对应不同的剑术和法术,而一种剑术或法术可以击败多种怪物。只要Cubic得到某怪物的信息,他就可以知道对应的剑术和法术,不会只知其一。

令国王感到欣慰的是亚特兰蒂斯最优秀的剑术师Xixi和最优秀的法术师Haha决定挺身而出,保卫国家。但问题是他们并不会这些特定的剑术和法术,必须抓紧时间学习。由于学习新剑术或新法术需要闭关,为防止Bug魔王偷袭,必须留下一个负责看守,因此两人不能同时学习。学习一种剑术或一种法术的时间都是1。现在的问题是他们到底需要学习那些剑术和法术,才能在最短的时间内拥有打败所有怪物的能力呢?

聪明的Cubic国王也发愁了,于是他决定聘请更聪明的你为亚特兰蒂斯的顶级技术顾问(相当于副科级),来帮他解决这个问题,全靠你了。

输入格式:输入有多组数据。对于每组数据:

1行是3个整数P1<=P<=10000),N0<=N<=500)和M0<=M<=500),分别表示怪物、剑术和法术的种数。接下来P行,每行一个字符串,分别表示P种怪物的名字。

再接下来N+M行,表示一种剑术或法术的信息。前N行是剑术,后M行是法术。对于每一行,首先是一个字符串,表示剑术或法术的名字;然后是一个非负整数K(K<=P),表示这种剑术或法术可以击败的怪物种数;然后就是K个字符串,表示K种怪物的名字。这些怪物都在上面的P种怪物之内。

所有的字符串都只由字母和连字符(-)组成,长度不超过15。两个字符串以空格隔开。所有字符串都不相同。

最后1行有3-1,表示输入结束。

输出格式:对于每组数据,输出一个整数,表示要拥有打败所有怪物的能力所需要的最少时间。如果无法打败所有的怪物,输出Poor Atlantis!”。

Savior.in

Savior.out

5 3 2

ant

rocking

kinfkong

savior

magicpig

AlongNineSword 2 ant kinfkong

HuaShanSword 2 rocking savior

TaijiSword 1 magicpig

Wind_walk 3 ant rocking magicpig

Blade-storm 2 kinfkong savior

-1 -1 -1

 

2



拯救亚特兰蒂斯行动以失败告终

这道题是个二分图求最大匹配,刚看到的时候在想我是不是应该把monster和sword&&magic连起来(当时内心:他这不是存心搞我,为什么要搞个sword又搞个magic),后来才听说是把sword和magic连起来,monster作为边,利用二分图最大匹配等于最小点覆盖来做。

我的想法还是太朴素,构建出g[i][j]数组即剑术i和法术j能杀同一种怪物,为了求这个我先建了两个数组每种剑术能杀的怪物,每种法术能杀的怪物,最后再导出g数组,时间复杂度疯狂上升,附上终极原始之丢人代码,开始拿了个60分到80分(这cena是真实不稳)

#include<stdio.h>
#include<stdlib.h>
FILE*fin,*fout;
int p,n,m;
char sword[501][15]={0},magic[501][15]={0},monster[10001][15]={0};
char s1[1000]={0},s2;
int num1[10005]={0},num2[10005]={0},dk[505][10000]={0},dk2[505][10000]={0},g[505][505]={0},y1[10005]={0},link[10005]={0};
int find(int v)
{
    int i,flag=0;
    for(i=1;i<=n;i++)
      if(g[v][i]&&y1[i]==0)
      {
      y1[i]=1;
        if(link[i]==0||find(link[i]))
        {
        link[i]=v;
        flag=1;
        return flag;}
      }
    return 0;
}
int main()
{
    int i,j,num=0,k=0,ans=0,ans2=0;
    fin=fopen("savior.in","r");
    fout=fopen("savior.out","w");
    while(1)
    {
    fscanf(fin,"%d%d%d",&p,&n,&m);
    ans=0;ans2=0;
    memset(num1,0,sizeof(num1));
    memset(num2,0,sizeof(num2));
    memset(g,0,sizeof(g));
    memset(sword,0,sizeof(sword));
    memset(magic,0,sizeof(magic));
    memset(monster,0,sizeof(monster));
    memset(s1,0,sizeof(s1));
    memset(link,0,sizeof(link));
    memset(dk,0,sizeof(dk));
    memset(dk2,0,sizeof(dk2));
    memset(y1,0,sizeof(y1));
    if(p==-1&&n==-1&&m==-1)
      break;
    for(i=1;i<=p;i++)
      fscanf(fin,"%s%c",&monster[i],&s2);
    for(i=1;i<=n;i++)
     {fscanf(fin,"%s%d",&sword[i],&num1[i]);
       for(j=1;j<=num1[i];j++)
        {fscanf(fin,"%s%c",&s1,&s2);
        for(k=1;k<=p;k++)
          if(!strcmp(s1,monster[k]))
           {dk[i][k]=1;break;}
        }
     }
    for(i=1;i<=m;i++)
     {fscanf(fin,"%s%d",&magic[i],&num2[i]);
       for(j=1;j<=num2[i];j++)
        {fscanf(fin,"%s%c",&s1,&s2);
        for(k=1;k<=p;k++)
          if(!strcmp(s1,monster[k]))
           {dk2[i][k]=1;break;}
        }
     }
    for(i=1;i<=n;i++)
      for(j=1;j<=p;j++)
      {
      if(dk[i][j]==1)
      {
      for(k=1;k<=m;k++)
        if(dk2[k][j]==1)
          break;
      if(dk2[k][j]==1)
         {g[k][i]=1;ans2++;}
      }
    }
    for(i=1;i<=m;i++)
    {
    for(j=1;j<=1000;j++)
      y1[j]=0;
    if(find(i))
      ans++;
    }
    if(ans2<p)
      fprintf(fout,"Poor Atlantis!\n");
    else 
      fprintf(fout,"%d\n",ans);
    }
    fclose(fin);
    fclose(fout);
    return 0;
}

隔壁dalao:我先来一手快排+二分。瞬间降低复杂度,在建立g数组这个地方时间复杂度仅略高于o(nlogn),闪电般的ac,可以说是教科书一般的操作了(好吧其实也没有那么夸张就是单纯自己没想到加菜而已)

附上闪电ac代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
FILE *fin,*fout;
struct moster{
	int n,a[3];
	char name[20];
};
struct moster a[10001];
struct sword1{
	char name[20];
	int n;
	int a[501];
};
struct sword1 sword[501];
struct magic1{
	char name[20];
	int n;
	int a[501];
};
struct magic1 magic[501];
int p,n,m,y[501],link[501],g[501][501];
char name[20];
void qsort1(int left,int right)
{
	int i,j;
	char mid[20];
	i=left;
	j=right;
	strcpy(mid,a[(i+j)/2].name);
	while(i<=j)
	  {
	  	while(strcmp(a[i].name,mid)<0) i++;
	  	while(strcmp(a[j].name,mid)>0) j--;
	  	if(i<=j) {a[0]=a[i];a[i]=a[j];a[j]=a[0];i++;j--;}
	  }
	if(i<right) qsort1(i,right);
	if(j>left) qsort1(left,j);
}
int find(int i)
{
	int j;
	for(j=1;j<=m;j++)
	   if(g[i][j] && !y[j])
	     {
	     	y[j]=1;
	     	if(!link[j] || find(link[j]))
	     	  {
	     	  	link[j]=i;
	     	  	return 1;
              }
		 }
	return 0;
}
int main(void)
{
	fin=fopen("savior.in","r");
	fout=fopen("savior.out","w");
	int i,j,num,k,x,x1,x2,mid,ans;
	char r;
	fscanf(fin,"%d%d%d",&p,&n,&m);
	while(p!=-1)
	  {
	  	for(i=1;i<=n;i++)
	  	   for(j=1;j<=m;j++)
	  	      g[i][j]=0;
	  	for(i=1;i<=m;i++)
	  	   link[i]=0;
	  	num=0;
	  	ans=0;
	  	for(i=1;i<=p;i++)
	  	  {
			fscanf(fin,"%c%s",&r,&a[i].name);
			a[i].n=0;
		  }
	  	qsort1(1,p);
	  	for(i=1;i<=n;i++)
	  	  {
			fscanf(fin,"%c%s%d",&r,&sword[i].name,&k);
			num=num+k;
			for(j=1;j<=k;j++)
			  {
			  	fscanf(fin,"%c%s",&r,&name);
			  	x1=1;
			  	x2=p;
			  	while(1)
			  	  {
			  	  	mid=(x1+x2)/2;
			  	  	if(strcmp(a[mid].name,name)==0) {x=mid;break;}
			  	  	if(strcmp(a[mid].name,name)<0) x1=mid+1;
			  	  	else x2=mid-1;
					}
			a[x].a[1]=i;
			  }
			}
	  	for(i=1;i<=m;i++)
	  	  {
			fscanf(fin,"%c%s%d",&r,&magic[i].name,&k);
			for(j=1;j<=k;j++)
			  {
			  	fscanf(fin,"%c%s",&r,&name);
			  	x1=1;
			  	x2=p;
			  	while(1)
			  	  {
			  	  	mid=(x1+x2)/2;
			  	  	if(strcmp(a[mid].name,name)==0) {x=mid;break;}
			  	  	if(strcmp(a[mid].name,name)<0) x1=mid+1;
			  	  	else x2=mid-1;
					}
			 	a[x].a[2]=i;
			 	g[a[x].a[1]][a[x].a[2]]=1;
			  }
			}
		for(i=1;i<=n;i++)
		  {
		  	for(j=1;j<=m;j++)
		  	   y[j]=0;
		  	if(find(i)) ans++;
		  }
		if(num==p)
		   fprintf(fout,"%d\n",ans);
		else
		   fprintf(fout,"Poor Atlantis!\n");
		fscanf(fin,"%d%d%d",&p,&n,&m);
	  }
	fclose(fin);
	fclose(fout);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值