poj3249Test for Job解题报告

14278251    h123120    3249    Accepted    10452K    1907MS    G++    2266B    2015-06-09 13:56:59

这是一道动态规划的题,也是我第一次做动态规划的题,卡了我10个小时,各种没考虑到的条件。。。。在也不相信百度了,说好的Dp,结果是拓扑排序+DP我也是醉了~~

废话不多说了先说说思路:

DAG上的路径问题,找它点权最大的路径。dp的状态量为:从u点出发到终点的最大权值d[u],状态转移方程为 d[u]=max(d[u],dp(v)+p[u]) (u,v)属于点集,p[u]为某个点的权值

但有以下几个点需要注意:

1.起点不是任意的点,需要入度为0!!!当然对应的终点需要出度为0,但是终点一定具有出度为0的属性,所以不需要管它,只需要在输入的时候统计点的入度即可。

2.这道题有10^5个结点,所以如果用邻接矩阵来储存图,开不了这么大的二维数组,所以需要邻接链表来储存图,我也是第一次用,以前只在数据结构上见过这东西,当时想有矩阵,谁会用这东西,然后有去学,不过别人的解题报告中这种光用数组来做链表的思路很巧妙啊,我还需要多多学习啊~~

3,接下来就是dp函数了,各种虐心啊,特殊值选取不定,

1)因为本题的权值可以为负值,所以选取一个很小的-INF来初始化状态量d,

2)然后各种细节,什么搜索到终点的flag,仔细体会为什么要有这个东西,

3)还有return时,忘了给终点赋值,因为终点的状态量的权值就是他本身的权值,不是前边的权值叠加,但是不知道他是不是终点,所以在搜的时候搜到终点再赋值,我在return那赋值的return d[u]=p[u];

其他的没什么了,自己还太菜啊,被个一般的dp题,困住了~~

附送一组数据,正确答案是13

10 10
-5
6
-1
-2
5
-9
7
-4
10
-12
1 2
1 3
2 3
3 6
3 7
7 10
7 8
8 9
8 10
ac代码(这个代码方便我调试,不怎么简洁,下面赋个简洁的):

#include <iostream>
#include<cstdio>
#include <cstring>
#include<algorithm>
#include<memory>
using namespace std;
const int maxN=100024;
const int maxM=1000024;
int n,m,e;
int p[maxN],head[maxN],d[maxN],in[maxN],vis[maxN];
//int next[maxN];
struct Edge
{
    int v,next;
}edge[maxM];
int dp(int u)
{
      // cout<<"中间点"<<u<<endl;
      if(vis[u])   {/*cout<<"已经得出过的值d["<<u<<"]:"<<d[u]<<endl;*/return d[u];}
      vis[u]=1;
      int& ans=d[u];
      ans=-(1<<30);
      int flag=0;
       for(int i=head[u];i!=-1;i=edge[i].next)//利用邻接链表
       {
                int v=edge[i].v;//依次访问的结点
                // ans=max(ans,dp(v)+p[u]);
                flag=1;
                int tmp=dp(v)+p[u];
           //   cout<<"临时状态量"<<"d["<<u<<"]:"<<tmp<<" "<<"当前结点:"<<v<<endl;
                  if(ans<tmp)//k可用max函数简化
                  {
                         ans=tmp;
                   //   cout<<"最终状态量:d["<<u<<"]:"<<ans<<endl;
                      //   next[u]=v;
                 }
            //    cout<<endl;
       }
 //      cout<<"flag:"<<flag<<endl;
      if(flag) {  /*cout<<"返回的状态量"<<"d["<<u<<"]:"<<ans<<endl;*/return ans;}
       else{/* cout<<"返回的状态量"<<"d["<<u<<"]:"<<p[u]<<endl;*/return d[u]=p[u];}
}
int main()
{
     freopen("1.txt","r",stdin);
        while(scanf("%d%d",&n,&m)==2)
        {
        for(int i=1;i<=n;++i)
            scanf("%d",&p[i]);//输入利润
        e=0;
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(in,0,sizeof(in));
    //    memset(next,0,sizeof(next));
          while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            in[b]=1;//入度不为0
            edge[e].v=b;//赋予它权值
            edge[e].next=head[a];//建立链表
            head[a]=e++;//他的表头指针 (用了之后再加)
        }
        for(int i=1;i<=n;i++)    d[i]=-(1<<30);//初始化它~~
        int temp=-(1<<30),first;
        for(int i=1;i<=n;i++)
        {
                if(in[i]==0)
                {
              //  cout<<"起点"<<i<<endl;
                first=i;
                temp=max(temp,dp(i));
                }
        }
   //     for(int i=first;i!=0;i=next[i])
       // cout<<i;
        //cout<<endl;
         printf("%d\n",temp);
       }
    return 0;
}

分割线----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
简化的代码:

#include <iostream>
#include<cstdio>
#include <cstring>
#include<algorithm>
#include<memory>
using namespace std;
const int maxN=100024;
const int maxM=1000024;
int n,m,e;
int p[maxN],head[maxN],d[maxN],in[maxN],vis[maxN];
struct Edge
{
    int v,next;
}edge[maxM];
int dp(int u)
{
      if(vis[u])   {return d[u];}
      vis[u]=1;
      int& ans=d[u];
      ans=-(1<<30);//特殊值注意!!
      int flag=0;
       for(int i=head[u];i!=-1;i=edge[i].next)//利用邻接链表
       {
                int v=edge[i].v;//依次访问的结点
                flag=1;
               ans=max(ans,dp(v)+p[u]);
        }
        if(flag) return ans;
       else return d[u]=p[u];//别忘了赋值啊
}
int main()
{
     freopen("1.txt","r",stdin);
        while(scanf("%d%d",&n,&m)==2)
        {
        for(int i=1;i<=n;++i)
            scanf("%d",&p[i]);//输入利润
        e=0;
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(in,0,sizeof(in));
          while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            in[b]=1;//入度不为0
            edge[e].v=b;//赋予它权值
            edge[e].next=head[a];//建立链表
            head[a]=e++;//他的表头指针 (用了之后再加)
        }
        for(int i=1;i<=n;i++)    d[i]=-(1<<30);//初始化它~~
        int temp=-(1<<30),first;
        for(int i=1;i<=n;i++)
        {
                if(in[i]==0)
                {
                first=i;
                temp=max(temp,dp(i));
                }
        }
         printf("%d\n",temp);
       }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值