奥赛奖金(DAG上的动态规划+记忆化搜索)

来源:互联网 发布:windows bat 相对路径 编辑:程序博客网 时间:2024/05/21 22:59

【问题描述】

  在2013年的NOIP复赛中,CQYZ高2015级信息学竞赛班的同学们以超强的实力,力压BS,NK,BZ等学校。学校大老板Mr.lu心情很好,决定给每位学生发奖金。并按每个人竞赛的成绩高低计算他们得到奖金的多少,成绩低的肯定要比成绩高的少,但奖金最少为100元。

  但Mr.lu又是一个很@的人,想发出的奖金尽量的少,这让Mr.He很生气。决定给老板出点难题,不将每个同学的成绩直接给他,而是只告诉他:学生a的成绩比学生b的成绩高。这可难坏了财务室的那几爷子!请你来帮助她们。

  注意:每人得到的奖金必须是整数元

【输入格式】

  第一行两个整数N,M,N表示学生总数;以下M行,每行2个整数a,b,表示学生a的竞赛成绩学生b高。注意,输入信息中不会出现a比b高,b比c高,c又比a高的情况。

【输出格式】

输出一个数表示最少总奖金。

【输入样例】

8 9
1 3
1 7
2 3
2 4
3 4
4 5
4 6
8 6
7 8

【输出样例】

812

【数据范围】

80%的数据满足n<=1000,m<=2000;
100%的数据满足n<=10000,m<=100000。

自己学校出的题,背景充满着某中华名校的乡土气息,由于Mr.He这个大佬(doubi)其实是一个DAG图(把每个学生看作是一个结点)的提供者,无私地为你提供了一张完美的DAG图(由高分结点指向低分结点),现在你要干的事情其实就是在给定的这张图上找一个拓补序列并使得最后每个结点的权值和最小。又由于所有奖金都是整数,所以把一开始出度为0的点搞成100,然后进行DAG上的动归。
设状态函数f(i) = 第i位学生的得到的最少奖金
状态转移方程为f(i)=max{f(j) | 1<=j<=n && i和j之间有一条有向边相连 }+1;
由于用填表的方法不好判断i,j之间边的关系,故用记忆化搜索实现。

简化代码小技巧:1.把标记的vis[]直接改成用初值被赋值为-1的dp[]来判断是否取之前的解
2.由于f(i)>=100,故可设变量t=99,直接开始动归,省去找出度=0结点的步骤

#include<cstdio>#include<iostream>#include<cstring>#include<queue>#include<vector>using namespace std;const int maxn=10005;vector<int>g[maxn];int n,m;int d[maxn];void init(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    {    int x,y;    scanf("%d%d",&x,&y);    g[x].push_back(y);    }}int f(int i)//f(i)=max{f(j) |  1<=j<=n i->j }+1{    int sz=g[i].size();    if(d[i]!=-1) return d[i];    int t=99;    for(int k=0;k<sz;k++)    {        int j=g[i][k];        t=max(t,f(j));    }    d[i]=t+1;    return d[i];}int main(){    //freopen("my.in","r",stdin);    //freopen("my.out","w",stdout);    int ans=0;    init();    memset(d,-1,sizeof(d));    for(int i=1;i<=n;i++)    ans+=f(i);    printf("%d",ans);    return 0;}
0 0
原创粉丝点击