UVa 10603 BFS+优先队列

【题目链接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19527

【解题报告】
lrj紫书中路径寻找问题的例题。大部分细节书中都有说明,不再赘述。说一点自己的感想。
书里面说算法的正确性不是显然的,可是我觉得应该是显然的吧?

每次找到队列里dist值最小的状态,以它来更新ans值,并且拓展其他状态。
假如当前我们找到了一个恰好有某个瓶子里的水为d升的状态p,现在我们把它push进队列。有两种情况:

1.p在队列首,说明它是dist值最小的状态,可以直接取出,然后结束搜索。

2.p在队列中。那么p后面的所有状态都可以砍去,因为无论如何都不会比
p状态更优。p前面的节点会优先被拓展,那么拓展之后比p更优的状态还会在p前面。所以如果p不是最优解,p一定不会被取出,反之,我们取出的第一个满足某个v[i]=d的状态一定是最优解。

不知道这样想有没有漏洞。如果有的话还请不吝指出。

【参考代码】

/*
这个题可以学到几个技巧:
1.struct状态的整体赋值
2.如何标记vis值(这里挺巧妙的)
3.在能够得到的答案里,如何找到最接近d的值
4.在优先队列里,最好在取出节点的时候更新答案
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

int cap[3];

struct  state
{
      int v[3];
      int dis;
      bool operator < ( const state& a )const { return dis>a.dis; }
};

int ans[210];
int vis[ 210 ][210];

void update( const state& u )
{
      for( int i=0; i<3; i++ )
      {
            int d=u.v[i];
            if(  ans[d]==-1 || ans[d]>u.dis ) ans[d]=u.dis;
      }
}

void solve(  int a, int b, int c, int d )
{
      cap[0]=a; cap[1]=b; cap[2]=c;
      memset( ans,-1, sizeof ans );  //保存能达到的水量值
      memset(  vis, 0, sizeof vis );  //标记已经访问过的状态
      state p0;
      p0.v[0]=0; p0.v[1]=0; p0.v[2]=c; p0.dis=0;
      priority_queue<state>q;
      q.push(p0);
      vis[0][0]=1;
      while(  !q.empty() )
      {
            state now=q.top(); q.pop();  //每次取出dis值最小的状态
            update(  now );          //要在每次取出的时候更新,不能在推入的时候更新。即使推入的时候可以到达d但未必是最优解
            if(  ans[d]>0 )  break;
            for(  int i=0; i<3; i++ )
                  for( int j=0; j<3; j++ )  //从i向j倒水
            {
                  if(i==j)continue;
                  state next;
                  memcpy(  &next,&now,sizeof now );
                  int temp=min( now.v[i], cap[j]-now.v[j] ); //要么j倒满,要么i倒空
                   next.v[i]-=temp;
                   next.v[j]+=temp;
                   next.dis+=temp;
                   if( !vis[ next.v[0] ][ next.v[1] ]  ) {    vis[ next.v[0] ][ next.v[1] ]=1;  q.push( next );   }
            }
      }
      while( d>=0 )
      {
            if(  ans[d]>=0 ){   printf( "%d %d\n", ans[d],d); return;  }
            d--;
      }
}

int main()
{
      int T; cin>>T;
      while( T-- )
      {
            int a,b,c,d; scanf(  "%d%d%d%d",&a,&b,&c,&d );
            solve( a,b,c,d );
      }

      return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的小工具;种子转磁力链接的

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值