hrbust 1214 哈理工oj 1214 方格取数【BFS广搜+dp】

方格取数
Time Limit: 1000 MSMemory Limit: 65535 K
Total Submit: 106(40 users)Total Accepted: 42(36 users)Rating: Special Judge: No
Description

 设有N*N的方格图(N<=10),我们将其中的某些方格中填入正整数,而其他的方格中则放人数字0。如下图所示(见样例 ,黄色和蓝色分别为两次走的路线,其中绿色的格子为黄色和蓝色共同走过的): 


A
             
    13     6    
        7      
      14        
  21       4    
    15          
  14            
             
B


某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大


Input
 有多组测试数据,每组格式如下:
    第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
Output
与输入对应,有多组输出,每组只需输出一个整数,表示2条路径上取得的最大的和。
Sample Input
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
Sample Output
67
Source
NOIp2000提高组
Recommend
黄李龙

很难贪心到点的一个题。最开始是这样想的:BFS找出第一条路,然后DP找出第二条路,为什么不两次dp呢?bfs记录路径是很方便的。每一条路最贪心去做。样例过了,但是WA了、

原因是这样的:

我输入了一组这样的数据:

5

1 1 1 1 1

1 1 1 1 2

1 2 1 1 1

1 1 1 1 1

1 2 1 1 1

第一遍走的路径是:(1,1)(1,2)(2,2)(3,2)(4,2)(5,2)(5,3)(5,4)(5,5)。第一次搜之后确实是保证了第一次的局部最优,第二次DP也保证了第二次的局部最优,但是整体最优并没有保证,所以这个思路是不够贪心的。

所以正确的思路应该是这样的:BFS漫无目地的去搜,对于任何一条路径,搜到了终点的时候,就dp一次,这样就保证了整体最优。

思路:BFS搜到终点,记录路径,然后路径上所有点变成0,然后DP一下,然后再把路径上原来的值赋回去、最终总能有最优解产生。

我们分步来详解:
首先是变量:

#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
struct zuobiao
{
    int cont;
    int jilu[30][2];//记录BFS路径
    int x,y,output;
}now,nex;
int a[15][15];//图
int fx[2]={0,1};//两种走法
int fy[2]={1,0};
int dp[15][15];//dp用
int jilu3[30];//记录变成0的点的值,恢复用
int n;
int maxn;//最终解
广搜+dp的核心部分代码:
void bfs(int x,int y)//起点未处理.
{
    now.x=x;
    now.y=y;
    now.output=0;
    for(int i=0;i<30;i++)
    {
        now.jilu[i][0]=-1;
        now.jilu[i][1]=-1;
    }
    now.cont=0;
    queue<zuobiao >s;
    s.push(now);//初始化
    while(!s.empty())
    {
        now=s.front();
        nex=now;
        if(now.x==n&&now.y==n)//如果搜到了终点、
        {
                for(int i=0;i<now.cont;i++)//把这条路径上的点变成0
                {
                    int x=now.jilu[i][0];
                    int y=now.jilu[i][1];
                    jilu3[i]=a[x][y];//同时用这个数组记录这么些个值。
                    a[x][y]=0;
                }
                memset(dp,0,sizeof(dp));//初始化dp数组
                for(int i=1;i<=n;i++)
                {
                    for(int j=1;j<=n;j++)
                    {
                        dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j-1]+a[i][j]);//dp的状态转移方程。
                    }
                }
                for(int i=0;i>now.cont;i++)//再赋值回去
                {
                    int x=now.jilu[i][0];
                    int y=now.jilu[i][1];
                    a[x][y]=jilu3[i];
                }
                if(now.output+dp[n][n]>maxn)//更新最优解
                {
                    maxn=now.output+dp[n][n];
                }
        }
        s.pop();
        for(int i=0;i<2;i++)//两种走法
        {
            nex.x=now.x+fx[i];
            nex.y=now.y+fy[i];
            if(nex.x>=1&&nex.x<=n&&nex.y>=1&&nex.y<=n)//如果当前走到的位子符合条件
            {
                nex.output=now.output+a[nex.x][nex.y];
                if(a[nex.x][nex.y]!=0)//如果这个点的值不为0
                {
                    nex.jilu[now.cont][0]=nex.x;
                    nex.jilu[now.cont][1]=nex.y;//记录路径
                    nex.cont=now.cont+1;
                }
                s.push(nex);
            }
        }
    }
}
完整的AC代码:
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
struct zuobiao
{
    int cont;
    int jilu[30][2];
    int x,y,output;
}now,nex;
int a[15][15];
int fx[2]={0,1};
int fy[2]={1,0};
int dp[15][15];
int jilu3[30];
int n;
int maxn;
void bfs(int x,int y)//起点未处理.
{
    now.x=x;
    now.y=y;
    now.output=0;
    for(int i=0;i<30;i++)
    {
        now.jilu[i][0]=-1;
        now.jilu[i][1]=-1;
    }
    now.cont=0;
    queue<zuobiao >s;
    s.push(now);
    while(!s.empty())
    {
        now=s.front();
        nex=now;
        //printf("%d %d %d\n",now.x,now.y,now.output);
        if(now.x==n&&now.y==n)
        {
                for(int i=0;i<now.cont;i++)
                {
                    int x=now.jilu[i][0];
                    int y=now.jilu[i][1];
                    jilu3[i]=a[x][y];
                    a[x][y]=0;
                }
                memset(dp,0,sizeof(dp));
                for(int i=1;i<=n;i++)
                {
                    for(int j=1;j<=n;j++)
                    {
                        dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j-1]+a[i][j]);
                    }
                }
                for(int i=0;i>now.cont;i++)
                {
                    int x=now.jilu[i][0];
                    int y=now.jilu[i][1];
                    a[x][y]=jilu3[i];
                }
                if(now.output+dp[n][n]>maxn)
                {
                    maxn=now.output+dp[n][n];
                }
        }
        s.pop();
        for(int i=0;i<2;i++)
        {
            nex.x=now.x+fx[i];
            nex.y=now.y+fy[i];
            if(nex.x>=1&&nex.x<=n&&nex.y>=1&&nex.y<=n)
            {
                nex.output=now.output+a[nex.x][nex.y];
                if(a[nex.x][nex.y]!=0)
                {
                    nex.jilu[now.cont][0]=nex.x;
                    nex.jilu[now.cont][1]=nex.y;
                    nex.cont=now.cont+1;
                }
                s.push(nex);
               // printf("%d\n",nex.output);
            }
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                a[i][j]=0;
            }
        }
        while(1)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(x==0&&y==0&&z==0)break;
            else a[x][y]=z;
        }
        maxn=0;
        bfs(1,1);
        printf("%d\n",maxn);
    }
}











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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值