[???]CC分配防晒霜

[题目]

 [???]CC分配防晒霜

 [???]CC分配防晒霜

 [???]CC分配防晒霜

[报告]

    从题目中“每个CC只能涂某一个瓶子里的防晒霜”,可以大概猜到算法——二分图匹配。

    的确,这道题是二分图匹配。图的左边是CC,右边是防晒霜。若某CC能用某防晒霜,那么就在这两者之间连线。当然,对于每个防晒霜,有必要而且是必须拆开——最后使所有防晒霜只能用1次——但这样会产生大量的防晒霜,这样显然不能在规定的空间内完成。所以必须处理。

    第一种想法是:用防晒霜去匹配CC。因为每个CC都是唯一的,所以这个几乎可以直接套用经典的是匈牙利算法。不过这样的时间受不了——鄙人拿了70分。

    在对第一种想法苦思冥想了3个小时,加了N多优化,仍然TLE,于是第二种想法应运而生:用CC去匹配防晒霜!——不过编程复杂度有点高……

    同样以匈牙利算法为模板:枚举每一个CC,尝试着去匹配防晒霜。若没有现成的防晒霜(可以用的被别人用光了),那么就只能去和别人换。怎么换呢?枚举该CC可以用的所有的防晒霜,尝试着对这些防晒霜的主CC提出要求。那么被提出的CC就会按照匹配的方式去寻找,若他经过上面一番努力,仍无法获得,显然他是不会换的。那么就只能去找另一个。若所有的都不换,那么只能认为该CC悲剧了,就跳过它。

    然后,90分——查数据后发现,那组数据的防晒霜的种类很少,防晒霜的总个数很多,而鄙人用的是HASH存储,不TLE才怪。怎么办呢?把HASH换成链表(或近似链表的东西)。

    至此,该题目圆满AC……

[程序]

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdio>
#define N 2500
#define M 1000
#define nil NULL
using namespace std;
ifstream fin ("tanning.in");
ofstream fout ("tanning.out");
class link
{
 public:
  long dex;
  link*next;
  link(long dd=0,link*nn=nil)
  {
   dex=dd;next=nn;
  }
}*t[M+1];
long n;
long ans,dx;
long a[N+1],b[N+1];
long s[M+1];
long f[N+1];
bool q[M+1];
long to[M+1];
static inline void join(long a,long b)      

{
 link*p=new link(b,t[a]);
 t[a]=p;
}
static inline void calc()                    // 贪心初始解
{
 for (long i=1;i<=n;i++)
  for (long c=a[i];c<=b[i];c=to[c])
   if (s[c])
   {
    f[i]=c;
    link*p=new link(i,t[c]);
    t[c]=p;
    s[c]--;
    ans++;
    break;
   }
}
static inline bool solve(long dex)
{
 for (long c=a[dex];c<=b[dex];c=to[c])           // 扫描目前可以直接放入的空
  if (s[c])
  {
   f[dex]=c;
   link*p=new link(dex,t[c]);
   t[c]=p;
   s[c]--;
   return true;
  }
 for (long c=a[dex];c<=b[dex];c=to[c])
  if (!q[c])
  {
   q[c]=true;
   for (link*p=t[c];p;p=p->next)
    if (solve(p->dex))
    {
     f[dex]=c;
     p->dex=dex;
     return true;
    }
  }
 return false;
}
int main(int argc, char *argv[])
{
 memset(a,0,sizeof(a));
 memset(b,0,sizeof(b));
 memset(to,0,sizeof(to));
 fin >> n >> dx;
 for (long i=1;i<=n;i++)
  fin >> a[i] >> b[i];
 memset(s,0,sizeof(s));
 for (long c,ff;dx>0;dx--) 
 {
  fin >> c >> ff;
  s[c]+=ff;
 }
 for (long i=M;i>=0;i--)
  t[i]=nil;
 to[M]=M+1;
 for (long i=M-1;i>=0;i--)
  if (s[i+1]) to[i]=i+1;
  else to[i]=to[i+1];
 ans=0;
 memset(f,0,sizeof(f));
 calc();
 for (long i=1;i<=n;i++)
  if (!f[i])                  // 未被匹配
  {
   memset(q,0,sizeof(q));
   if (solve(i)) ans++;
  }
 fout << ans << endl;
    return EXIT_SUCCESS;
}

转载于:https://www.cnblogs.com/klarkxy/archive/2010/10/05/10017165.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值