uva 1380 - A Scheduling Problem 一个调度问题 好难的动态规划


想不出来啊!!!,紫书上写的很清楚。


1.利用题目给出的图论定理,先不考虑无向边,假如图中有向链的最大长度为K,那么答案为K+1或K+2(注意是K链的长度,答案是链包含点的个数)。


2.考虑答案是否能为K+1,否则为K+2,即考虑给无向边定向,定向之后能否使最长链不超过K?


进行动态规划来考虑:

对于每一个结点

in[x] 表示: 在定向后最长链不超过K的前提下,终点为该结点的有向链中最大长度的最小值。

out[x]表示:在定向后最长链不超过K的前提下,起点为该结点的有向链中最大长度的最小值。


设置一个变量ok,赋初值=1,对于每一个点都要验证这个题目是否能满足条件,如果某一个点不能满足,ok=0。

如果ok最后==0,那么答案K+2,否则K+1。


动态规划可解的原因:


满足最优子结构、无后效性:


对于某一个结点,先验证他与子结点相连的边中的定向边,看看最长的出发链和最长的进入链长度之和能否<=K,

如果不行,ok=0;return;


否则,接下来对无向边进行定向,枚举无向边相连的子结点,以求in[x]为例,

in[x]={   定向边中进入链最大值, 不定向的边中将某些边定为进入方向 ,可取到的进入链的最小值      }  (只考虑与子结点相连的边)


按照未定向边连接的子结点的in[x]从小到大排序,按照此顺序逐一验证能否存在一条边,使最长进入链+最长出发链<=K,每验证一条边,该边包含前面的边都定向为进入边,若存在in[x]={   定向边中进入链最大值, 该条链的长度    }  (只考虑与子结点相连的边)


注意可能全部定向为出发边。


out[x]求法...



对于某一个结点,如果有定向边,通过定义 又不存在合法的进入链,那么in[x]=INF,

对于一个结点如果in[x]==out[x]==INF,那么ok=0,否则ok=1;

(对于一个结点,可能不存在合法的进入链,但可能存在合法的出发链,这样也能使之合法)


最优子结构:

每个结点算出来的in[x]、out[x]都是合法情况下通过规划方法给边定向所得进入链,出发链的最大长度最小值,所以如果把这些值带到父结点验算,仍然不能满足条件,那么肯定就不存在使父结点为子树满足条件的规划(定向)方法。


题目:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4126

点我



/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source: uva 1380 - A Scheduling Problem
 *   @type:  dp
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=200    ;
//const int maxV=12    ;
int n,K,now;
bool ok;

int in[maxn+4],out[maxn+4],root;
int dp[maxn+4][3];
int pre[maxn+4];
bool vis[maxn+4];
struct Edge
{
    int to;
    char type ;
    Edge(){}
    Edge(int to,char type):to(to),type(type){}

};
vector<Edge >G[maxn+4];

void init()
{
    for(int i=1;i<=maxn;i++) G[i].clear();
    memset(vis,0,sizeof vis);
}

void add_edge(int x,char *s)
{
    char type='o';
    int len=strlen(s);
    int sum=0;
    for(int i=0;i<len&&s[i]!='d'&&s[i]!='u';i++)
    {
        sum=sum*10+s[i]-'0';
    }

    if(s[len-1]=='d')  type='d';
    else if(s[len-1]=='u')  type='u';

    G[x].push_back(Edge(sum,type));
    n=max(n,sum);
    vis[sum]=1;

}

int getkind(char type)
{
    int kind;
    if(type=='o')  kind=0;
    else if(type=='d')  kind=1;
    else   kind=2;

    return kind;
}

void dfs(int x)
{
    dp[x][0]=dp[x][1]=dp[x][2]=0;
    for(int i=0;i<G[x].size();i++)
    {
        Edge & e=G[x][i];
        int y=e.to;
        char type=e.type;

        dfs(y);
        int kind=getkind(type);
        dp[x][kind]=max(dp[x][kind],dp[y][kind]+1);
    }
    K=max(K,dp[x][1]+dp[x][2]);

}


bool cmp(int x,int y)
{
    return in[x]<in[y];
}

bool cmp2(int x,int y)
{
    return out[x]<out[y];
}

void cal(int x)
{
    in[x]=out[x]=0;
    vector<int >ve;
    int maxin,maxout;
    for(int i=0;i<G[x].size();i++)
    {
        Edge& e=G[x][i];
        int y=e.to;
        char type=e.type;
        int kind=getkind(type);
        cal(y);

        if(kind==1)  out[x]=max(out[x],1+out[y]);

        else if(kind==2) in[x]=max(in[x],1+in[y]);

        else  ve.push_back(y);

    }
    if(in[x]+out[x]>K)  {ok=0;return;}
    if(ve.size()==0)   return;
    int m=ve.size();
    int tmpin=INF,tmpout=INF;
    sort(ve.begin(),ve.end(),cmp);
    pre[m]=0;
    pre[m-1]= out[ve[ m-1 ] ]  ;
    for(int i=m-2;i>=0;i--)
    {
        pre[i]=max(pre[i+1],out[ ve[i]  ]);
    }

    for(int i=0;i<m;i++)
    {
        if( in[ve[i]]+1+ max(out[x],pre[i+1]+1  )<=K  )
        {
            tmpin=max(in[x],in[ve[i]] +1);
            break;
        }
    }
    if( max(out[x],pre[0]+1  )+in[x]<=K)
    {
          tmpin=in[x];
    }

    sort(ve.begin(),ve.end(),cmp2);
    pre[m]=0;
    pre[m-1]= in[ve[ m-1 ] ]  ;
     for(int i=m-2;i>=0;i--)
    {
        pre[i]=max(pre[i+1],in[ ve[i]  ]);
    }
      for(int i=0;i<m;i++)
    {
        if( out[ve[i]]+1+ max(in[x],pre[i+1]+1  )<=K  )
        {
            tmpout=max(out[x],out[ve[i]] +1);
            break;
        }
    }

    if(max(in[x],pre[0]+1  )+out[x]<=K)
    {
        tmpout=out[x];
    }

    in[x]=tmpin;
    out[x]=tmpout;

    if(in[x]==INF&&out[x]==INF)  ok=0;
}

int main()
{
    char s[50];
    int x,y;char type;
    int cnt=0;

    while(~scanf("%d",&x)&&x)
    {

        init();
        n=x;

         while(~scanf("%s",s)&&strcmp(s,"0")!=0)
        {
            add_edge(x,s);
        }

        while(~scanf("%d",&x)&&x)
        {
            n=max(n,x);
            while(~scanf("%s",s)&&strcmp(s,"0")!=0)
           {
            add_edge(x,s);
           }
        }

        for(int i=1;i<=n;i++)  if(!vis[i])  root=i;
        K=0;
        dfs(root);
//        cout<<K<<endl;
        ok=1;
        cal(root);

        int ans=ok?K+1:K+2;
        printf("%d\n",ans);

    }



   return 0;
}
/*
1 2d  3u 0
0
1 2 3d 0
2 4d 0
3 5d 0
4 6d 0
0
1 2d 3u 4 0
0
1 2d 3 0
2 4d 5d 10 0
3 6d 7d 11 0
6 8d 9 12 0
0
1 2 3 4 0
2 5d 0
3 6d 0
4 7d 0
5 8d 0
6 9d 0
7 10d 0
0
0
*/


对于这个题目,状态的定义方法值得学习。其中的排序也非常关键,出彩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值