[雅礼中学2005分区联赛模拟试题]奖金_天涯浪子_新浪博客

[题目]

奖金(Reward.pas/exe)

【题目描述】

    由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出,Yali Company总经理Mr.Z心情好,决定给每位员工发奖金。公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。

    于是Mr.Z下令召开m方会谈。每位参加会谈的代表提出了自己的意见:“我认为员工a的奖金应该比b高!”Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元。

 

【输入】

    第一行两个整数n,m,表示员工总数和代表数;

    以下m行,每行2个整数a,b,表示某个代表认为第a号员工奖金应该比第b号员工高。

 

【输出】

    若无法找到合法方案,则输出“Poor Xed”;否则输出一个数表示最少总奖金。

 

【样例输入】

2 1

1 2

 

【样例输出】

201

 

【数据范围】

80%的数据满足n<=1000,m<=2000;

100%的数据满足n<=10000,m<=20000。

[成绩]

 

- [报告]

    看这个成绩,会很惊讶于第5个点这个0.12S,标程我测出来时0.01S的。原因就是使用了一个非多项式复杂度的答案调整——当时怕算出来的结论有错,结果发现根本没用……

    主算法很简单:拓扑排序。从题目中很明显可以看出一个拓扑结构:若A的奖金要高于B的,那么就从B连一条有向边到A。若这个图有环(无法拓扑排序),那么无解,否则对这个图产生的拓扑序列进行处理。

    处理方案一:按照拓扑序列一次染色递增——这个作为一组初始可行解,进行调整(这是我最初的做法)。

    处理方案二:动态规划(或许这个的级别不够,不过从动态规划的使用定义上来说是可以的)。对于拓扑序列进行DP。令F[I]表示在拓扑序列第I个的结点的颜色。则(打不出大的“{”……)

                100          (结点I没有前驱结点)

      F[I]=     max{F[K]+1}  (K为I的任意一个前驱结点)

       如果担心的话,可以对这组解进行调整(事实上我调整了)。

    至此,本题圆满解决。——当然可以直接搞出一组必然可行的解(比如所有的都染上20000),然后一直往下调整。如果调整算法好的话,AC也是容易的。

    鄙人的调整方法非常天真、朴素:对于每一个节点,把它往下调1,然后对它的前驱进行递归调整,直到调整出一组新解。否则宣告调整失败,宣布这个节点再也无法往下调整(利用这个可以记忆化一下,加速效果非常好)。

[程序]

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#define nil NULL             // 请忽略这些,这些是模板
#define N 10000
using namespace std;
ifstream fin ("reward.in");
ofstream fout ("reward.out");
class link
{
 public:
  long dex;
  link*next;
  link (long xx=0,link*nn=nil)
  {
   dex=xx;
   next=nn;
  }
}*t[N+1],*gt[N+1];
long n,m;
long c[N+1],col[N+1];
bool b[N+1];
static inline void join(link*&a,long b)
{
 link*p=new link(b,a);
 a=p;
}
bool flag;
static inline void dfs(long dex)
{
 col[dex]=1;
 for (link*p=t[dex];p&&flag;p=p->next)
  if (col[p->dex]==0)
   dfs(p->dex);
  else if (col[p->dex]==1)       //  有环
   flag=false;
 col[dex]=2;
 c[m--]=dex;
}
static inline void mon(long dex)
{
 for (link*p=t[dex];p;p=p->next)
  if (col[p->dex]<=col[dex])
  {
   col[p->dex]=col[dex]+1;
   mon(p->dex);
  }
}
static inline bool ok(long dex)
{
 if (b[dex]) return false;
 col[dex]--;
 if (col[dex]>=0)
 {
  long flag=true;
  for (link*p=gt[dex];p&&flag;p=p->next)
   if ((col[p->dex]<col[dex])||(ok(p->dex)));
   else flag=false;
  if (flag) return true;
 }
 col[dex]++;
 b[dex]=true;
 return false;
}
int main(int argc, char *argv[])
{
 fin >> n >> m;
 for (long i=1;i<=n;i++)
  t[i]=gt[i]=nil;
 for (long i=1,x,y;i<=m;i++)
 {
  fin >> x >> y;
  join(t[y],x);
  join(gt[x],y);
 }
 memset(c,0,sizeof(c));
 memset(col,0,sizeof(col));
 m=n;
 flag=true;
 for (long i=1;i<=n;i++)
  if (!col[i])
   dfs(i);
 if (flag)
 {
  memset(col,255,sizeof(col));                             // 制造可行解
  for (long i=1;i<=n;i++)
   if (col[c[i]]<0)
   {
    col[c[i]]=0;
    mon(c[i]);
   }
  memset(b,0,sizeof(b));
  for (long i=n;i>=1;i--)
   while (col[c[i]]>0&&ok(c[i]));        // 判断能否再往下缩减 
  m=100*n;
  for (long i=1;i<=n;i++)
   m+=col[i];
  fout << m << endl;
 }else
 {
  fout << "Poor Xed" << endl;
 }
 return EXIT_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值