山东省ACM第一届省赛试题

A: Balloons
题目大意:对于判定两个气球是否相邻,Saya and Kudo 有两种不同的观点,Saya认为对于 A  和 B  满足  时,可以认为两个气球相邻,对于Kudo, 当元素 A  和 B  满足    同时 时,两个气球相邻。问:以两人各自的观点,各有多少个气球连通块。
题目实质,用两次搜索。判断各有多少个连通块。
代码:
 

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

using namespace std;

const int maxn = 110;

char chr[maxn][maxn];
int vis[maxn][maxn];
int n;
int dis[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};

void dfs1(int r,int c,int id) {
    if(r<0 || r>=n||c<0||c>=n)
        return ;
    if(vis[r][c] > 0 || chr[r][c] != '1')
        return ;
    vis[r][c] = id;
    for(int dr = -1;dr<=1;dr++) {
        for(int dc = -1;dc<=1;dc++) {
            if(dr != 0 || dc!= 0)
                dfs1(r+dr,c+dc,id);
        }
    }
}

void dfs2(int r,int c,int id) {
    if(r<0 || r>=n||c<0||c>=n)
        return ;
    if(vis[r][c] > 0 || chr[r][c] != '1')
        return ;
    vis[r][c] = id;
    for(int dr = -1;dr<=1;dr++) {
        for(int dc = -1;dc<=1;dc++) {
            if(dr+dc==-1 || dr+dc==1 )
                dfs2(r+dr,c+dc,id);
        }
    }
}
int main() {
    int flag = 1;
    while(scanf("%d",&n) !=EOF) {
        if(n==0)
            return 0;
        getchar();
        for(int i = 0;i<n;i++) {
            scanf("%s",chr[i]);
        }
        memset(vis,0,sizeof(vis));
        int cnt=0;
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                if(vis[i][j] == 0 && chr[i][j] == '1')
                    dfs1(i,j,++cnt);

            }
        }
        memset(vis,0,sizeof(vis));
        int cnt2 = 0;
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                if(vis[i][j] == 0 && chr[i][j] == '1')
                    dfs2(i,j,++cnt2);

            }
        }
        printf("Case %d: ",flag++);
        printf("%d ",cnt2);
        printf("%d\n",cnt);
        printf("\n");
    }
}

C:  Shopping

题目大意:一排商店从最左端到最右端,再从最右端到最左端,问路程是多少。
解题思路一:对这一组数进行从小到大的排序,用后方的值减去前方相邻的值,最后用最后方的值减去最前方的值,将它们的和累加。
解题思路二:找到这组数中的最大值与最小值,两者相减再乘2。(这两种方法本质上一样,但方法二更简便,但是容易想不到)
代码一:

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

using namespace std;

int main() {
    long long   n;
    long long  arr[100000];
    while (scanf("%lld", &n) != EOF) {
        if (n == 0)
            return 0;
        for (int i = 0; i < n; i++) {
            scanf("%lld", &arr[i]);
        }
        sort(arr, arr + n);
        long long  sum = 0;
        int i;
        for (i = 1; i < n; i++) {
            sum = sum + arr[i] - arr[i - 1];
        }
        sum += arr[i - 1] - arr[0];
        printf("%lld\n", sum);
    }
    return 0;
}

代码二:

#include<iostream>
#include<cstdio>
#include <algorithm>
#include<cmath>

using namespace std;

int main() {
    long long   n;
    long long  arr[100000];
    while (scanf("%lld", &n) != EOF) {
        if (n == 0)
            return 0;
        for (int i = 0; i < n; i++) {
            scanf("%lld", &arr[i]);
        }
        sort(arr, arr + n);
        long long int sum = abs(arr[n - 1] - arr[0])*2;
        printf("%lld\n", sum);
    }
    return 0;
}

D:Emergency 
题目大意:第一行输入三个数N,M,Q 分别代表几个城市,有几条路(起始点,终止点,道路长度),几行操作指令(0或1两种)0表示x城市被夺回  1表示从x到y城市的最短路径,注意,只可以经过被收回的城市。
由三个0结束输入。
对于输出:若指令为1,当起始点或终止点有一个未被收回时,就输出  City XXX or YYY  is not available.

                                       当起始点与终止点均被收回,但中间的路不通时,就输出  No such path.
                                       可通就直接输出最短路的长度。

                 若指令为0,当0后方的城市不是第一次出现时,就输出      City  XXX  is already recaptured.
代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#define inf 0x3f3f3f3f
using namespace std;
int maap[500][500];
int vis[500];
int main()
{
    int u,v,w;//起始,终止,长度
    int N,M,Q;
    int cnt=1;
    while(scanf("%d%d%d",&N,&M,&Q)&&(N&&M&&Q))
    {
        printf("Case %d:\n",cnt++);
        /* 为标记两道路之间长度的数组赋初值*/
        for(int i=0;i<=N;i++)
          for(int j=0;j<=N;j++)
          {
            if(i==j) maap[i][j]=0;//自身到自身长度为0
              else maap[i][j]=inf;//到别处先设为无穷大
           }
           /* 标记道路是否已收回*/
           memset(vis,0,sizeof(vis));
        /*输入道路*/
         for(int i=0;i<M;i++)
         {
             scanf("%d%d%d",&u,&v,&w);
             maap[u][v]=min(w,maap[u][v]);//在这里就优先找到x到y距离的最小值
         }
         /*接下来为0 1操作*/
         int x,y,flag;
         for(int i=0;i<Q;i++)
         {
             scanf("%d",&flag);
             if(flag)//1操作
             {
                 scanf("%d%d",&x,&y);
                 if(vis[x]==0||vis[y]==0)
                   printf("City %d or %d is not available.\n",x,y);
                 else
                 {
                     if(maap[x][y]==inf) printf("No such path.\n");
                       else
                         printf("%d\n",maap[x][y]);
                 }
             }
             else//0操作
             {
                 scanf("%d",&x);
                 if(vis[x])
                    printf("City %d is already recaptured.\n",x);
                 else
                 {
                     vis[x]=1;
                     for(int i=0;i<N;i++)
                        for(int j=0;j<N;j++)
                          maap[i][j]=min(maap[i][x]+maap[x][j],maap[i][j]);

                 }
             }
         }

        printf("\n");
    }
    return 0;
}

E: Fairy tale 

这题猛一看以为是搜索


 

F:   Greatest Number

题目大意:给n个数,最多可以挑4个数,这四个数可以挑选重复的值,问:在和不超过M的前提下,所能得到的最大值。
解题思路:做题时一直认为是多重背包,但这里数值太大,开不了这么大的数组。
实际上是利用了数之间的组合以及二分。
代码如下:
 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int cmp(int a,int b)
{
    return a<b;
}
int main()
{
    int n,m;
    int a,b[1010];
    int sum[100010];//存任意两个数之间的和
    int cas=1;
    while(scanf("%d%d",&n,&m)&&n&&m)
    {
        b[0]=0;//合法的第一个数记为0//它的存在,使每个数自身能在sum数组中存在
        int cnt=1;int k=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            if(a<m)
                b[cnt++]=a; //筛选
        }
        for(int i=0;i<cnt;i++)//由于b数组第一个0的存在,这里数组sum[]内含有一个数本身,还有任两个数的和(不同数和相同数的)
          for(int j=i;j<cnt;j++)
          {
             if(b[i]+b[j]<=m)
                sum[k++]=b[i]+b[j];
          }
         
          sort(sum,sum+k,cmp);
         
        /*
            上方已完成1个数的单独组合或者是两个数之间的组合,因为这里最多可以找四个数进行累加,
            所以可以对sum[]数组再进行一次组合,找到最终的最大值
            用二分法进行查找
        */
        int maxx=0;
        for(int i=0;i<k;i++)
        {
            int low=i;int high=k-1;//注意这里的low=i
            while(low<=high)
            {
                int mid=(low+high)/2;
                if(sum[i]+sum[mid]>m)
                   high=mid-1;
                else
                {
                    maxx=max(maxx,sum[i]+sum[mid]);
                    low=mid+1;
                }

            }
        }
        cout<<"Case "<<cas++<<": "<<maxx<<endl;//冒号后有空格,因为这个wr
        cout<<endl;

    }
    return 0;
}

G: Hello World!

题目大意:
输入n行数据,每一行的两个元素代表某一值的横坐标与纵坐标。在这n行数据中,找到一个横坐标与纵坐标值均大于它的。多种输出方案优先选择行最小的情况,其次选择列最小的,若找不到,则输出-1  -1 ,以0行结束,每个测试案例后均有一个空行。
代码:
 

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

using namespace std;
const int maxn=1010;
struct node {
    int x, y;
};
node a[maxn], arr[maxn];

bool cmp(node a, node b) {
    if (a.x == b.x)
        return a.y < b.y;
    return a.x < b.x;
}

int main() {
    int n;
    int flag = 1;
    while (scanf("%d", &n) != EOF) {
        if(n==0)
            return 0;
        for (int i = 1; i <= n; i++)
            scanf("%d%d", &a[i].x, &a[i].y);
        for (int i = 1; i <= n; i++) {
            arr[i].x = a[i].x;
            arr[i].y = a[i].y;
        }
        printf("Case %d:\n",flag++);
        sort(a + 1, a + 1 + n, cmp);
        bool t = false;
        int i,j;
        for (i = 1; i <= n; i++) {
            t=false;
            for (j = 1; j <= n; j++) {
                if (a[j].x > arr[i].x && a[j].y > arr[i].y) {
                    printf("%d %d\n",a[j].x,a[j].y);
                    t=true;
                    break;
                }
            }
            if(j==n+1 && !t) {
                printf("-1 -1\n");
            }
        }
        printf("\n");
    }
    return 0;
}

H:   Ivan comes again!
题目大意:G题的升级版
find操作与G题意思相同,这里add操作是增加一个元素,两值代表横纵坐标;remove操作是删除一个元素,两值代表横纵坐标。
set<pair<int,int>  >S;  默认比较规则先按照first  排序,再按照second排序!

代码如下:

这里数值太大,相同代码提交有时AC,有时就TLE。

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
struct node
{
    int first,second;
}p;
bool operator <(node  a,node  b)
{
   if(a.first == b.first) return a.second < b.second;
	return a.first < b.first;
}
int main()
{
    //pair<int,int>p;
    int n;
    int cas=1;
    char ch[10];
    //set< pair<int,int> >s;
    set<node>s;
    set<node>::iterator it;

    while(cin >> n && n)
    {
        s.clear();
        printf("Case %d:\n", cas++);

        for(int i=0;i<n;i++)
        {
          scanf("%s",ch);
          scanf("%d%d",&p.first,&p.second);
          if(ch[0]=='a')
                s.insert(p);
          else if(ch[0]=='r')
                s.erase(p);
          else
          {
              for(it = s.lower_bound(p);it!=s.end();it++)
              {
                  if(it->first > p.first && it->second > p.second)
                  {
                      printf("%d %d\n",it->first,it->second);
                      break;
                  }
              }
              if(it==s.end())
                printf("-1\n");
          }

        }
        printf("\n");
    }
    return 0;
}

通过这个题,又知道了 set<pair<int,int>  >S这个东西.

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值