程序设计实习上机练习43 至少有多少只恼人的大青蛙?(搜索+贪心,至今未解决)

程序设计实习上机练习43 至少有多少只恼人的大青蛙?(搜索+贪心,至今未解决)
总时间限制: 1000ms 内存限制: 65536kB

描述
有一种大青蛙会跳越稻田,从而踩踏稻子。农民在早上看到被踩踏的稻子,希望知道晚上有多少青蛙穿越自家的稻田。每只青蛙总是沿着一条直线跳跃稻田,而且每次跳跃的距离都相同。
这里写图片描述
如图1和图2所示,稻田里的稻子组成一个栅格,每棵稻子位于一个格点上。而青蛙总是从稻田的一侧跳进稻田,然后沿着某条直线穿越稻田,从另一侧跳出去。
这里写图片描述
如图3所示,可能会有多只青蛙从稻田穿越。青蛙的每一跳都恰好踩在一棵水稻上,将这棵水稻踩坏。有些水稻可能被多只青蛙踩踏。当然,农民所见到的是图4中的情形,并看不到图3中的直线,也见不到别人家田里被踩踏的水稻。需要注意的是,农民可以根据水稻踩坏的程度来精确计算出一共几只青蛙踩踏过这一棵水稻,所以聪明的农民就能得到图4的水稻毁坏程度图。

我们可以认为每条青蛙跳跃路径上至少包括3棵被踩踏的水稻。而在一条青蛙跳跃路径的直线上,也可能会有些被踩踏的水稻不属于该行走路径,例如青蛙跳跃路径(2,3)(4,5)(6,7)经过(3,4),而(3,4)不属于跳跃路径。

注意:同一只青蛙不会第二次跳入同一片稻田,可能存在不同的两只青蛙的跳跃路径完全一样。

现在给定水稻破坏程度图,请你写一个程序,确定:最少有多少只青蛙穿越了稻田。例如,图4的答案是4,图3所示的跳跃路径数量就是答案。输入数据保证有解且最多14只青蛙穿越稻田。

输入
从标准输入设备上读入数据。第一行为测试数据数目。每组测试数据第一行上两个整数R、C,分别表示稻田中水稻的行数和列数,1≤R、C≤50。第二行是一个整数N,表示被踩踏的水稻数量, 3≤N≤700。在剩下的N行中,每行有三个整数,分别是一颗被踩踏水稻的行号(1~R)、列号(1~C)和被踩踏次数,三个整数之间用空格隔开。每棵被踩踏水稻只被列出

输出
对于每一组测试数据从标准输出设备上输出一个整数,表示最少有几只青蛙穿越了稻田。

样例输入

1
6 7
14
2 1 1
2 2 1
2 3 2
2 4 1
2 5 1
2 6 2
2 7 1
3 4 1
4 2 1
4 5 1
6 1 1
6 3 1
6 5 1
6 7 2

样例输出

4

本题首先发现朴素的贪心是不对的,每一次挑出最长路径并不能保证青蛙个数最少。
那么只能考虑搜索,合理安排搜索顺序来实现剪枝,规定从编号小的开始搜,这个也容易想到。搜索时候引入的另一个剪枝就是容易发现一个青蛙最多max{r,c}步,剩下离当前最小肯定走不完时候时没必要再算,退出。
然而这还是TLE,为了让当前最小收敛的快一点,结合贪心,先搜步数多的(A*搜索),但是还是TLE。
至今为止,没有办法了。
后来我考虑到有很大可能最小值每一步都保证是当前的最优解(当然这个贪心是不对的),WA,那么有更大概率是当前的前几个优秀解,结果因为数据太弱,AC。
但是,本题至今未解决,只给出一个近似解,网上也没有题解,poj上做出的人也只有一两个,所以暂时没法写对,先拿这个cheat的题解mark一下,以后再慢慢学习。

Accepted    396kB   170ms   2754 B  G++
#define TEST
#undef TEST

#define MAX_SIZE 50
#define MAX_N 700
#define INF 14

#include<stdio.h>
#include<memory.h>
#include<stdlib.h>

typedef struct
{
    int dx,dy,k;
}
record_type;

typedef struct
{
    int x,y,times;
}
point_type;

int cases,r,c,n,min;
int t[MAX_SIZE+1][MAX_SIZE+1],total;
point_type p[MAX_N];

int compare_point(const void* e1,const void* e2)
{
    const point_type* p1=(const point_type*) e1;
    const point_type* p2=(const point_type*) e2;
    if (p1->x!=p2->x)
        return p1->x-p2->x;
    else
        return p1->y-p2->y; 
} 

inline int dis(int x,int y)
{
    return x*x+y*y;
} 

int compare_record(const void* e1,const void* e2)
{
    const record_type* p1=(const record_type*) e1;
    const record_type* p2=(const record_type*) e2;
    if (p2->k!=p1->k)
        return p2->k-p1->k;
    else
        return dis(p1->dx,p1->dy)-dis(p2->dx,p2->dy); 
}

inline bool is_inside(int x,int y)
{
    return (1<=x && x<=r && 1<=y && y<=c);
}

void dfs(int now,int point)
{
    if ((min-now)*r<total && (min-now)*c<total)
        return;
    int dx,dy,k,num_j=0,i0,last,l=0;
    record_type record[MAX_N];
    #ifdef TEST
    printf("now=%d\n",now);
    for (int i=1;i<=r;i++)
        for (int j=1;j<=c;j++)
            printf("%d%c",t[i][j],j==c?'\n':' '); 
    #endif
    if (total==0)
    {
        if (now-1<min) 
            min=now-1;
        return;
    }
    for (int i=point;i<=n;i++)
      if (t[p[i].x][p[i].y])
      {
        for (int j=i+1;j<=n;j++)
          if (t[p[j].x][p[j].y])    
          {
            dx=p[j].x-p[i].x;
            dy=p[j].y-p[i].y;
            if (is_inside(p[i].x-dx,p[i].y-dy))
                continue;
            for (k=2;;k++)
            {
                if (is_inside(p[i].x+k*dx,p[i].y+k*dy))
                {
                    if (t[p[i].x+k*dx][p[i].y+k*dy]==0)
                    {
                        k=-1;
                        break;
                    }
                }
                else
                    break;
            }
            if (k>2)
            {
                record[num_j].k=k;
                record[num_j].dx=dx;
                record[num_j].dy=dy;
                num_j++;
            }
          }
        i0=i;
        break;
      }
    qsort(record,num_j,sizeof(record_type),compare_record);
    last=0;
    for (int i=0;i<num_j;i++)
    {
        if (record[i].k<record[last].k)
        {
            last=i;
            l++;
            if (l>=2)
                return;
        }
        //贪心求近似解 
        dx=record[i].dx;
        dy=record[i].dy;
        k=record[i].k;
        for (int i1=0;i1<k;i1++)
            t[p[i0].x+i1*dx][p[i0].y+i1*dy]--;
        total-=k;
        if (t[p[i0].x][p[i0].y])
            dfs(now+1,i0);
        else
            dfs(now+1,i0+1);
        for (int i1=0;i1<k;i1++)
            t[p[i0].x+i1*dx][p[i0].y+i1*dy]++;
        total+=k;
    }
    return;
}

int main()
{
    #ifdef TEST
    freopen("input.txt","r",stdin); 
    freopen("output.txt","w",stdout); 
    #endif
    scanf("%d",&cases);
    while (cases--)
    {
        min=INF;
        scanf("%d%d%d",&r,&c,&n);
        memset(t,0,sizeof(t));
        total=0;
        for (int i=0;i<n;i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].times);
            t[(p+i)->x][(p+i)->y]=(p+i)->times;
            total+=(p+i)->times;
        }
        qsort(p,n,sizeof(point_type),compare_point);
        dfs(1,0);
        printf("%d\n",min);
    }
    return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值