2018年2月8日训练日记

明明思路对了,却是TLE。。。(经过改正变成了RE)

无奈之下,又去翻了饶齐大佬的博客,具体内容如下:

HDU 1853 Cyclic Tour(最小费用流)
题意:
给你一个N个点M条边的带权有向图,现在要你求这样一个值:
该有向图中的所有顶点正好被1个或多个不相交的有向环覆盖(每个节点只能被一个有向环包含).
这个值就是 所有这些有向环的权值和. 要求该值越小越好.

分析:

下面用费用流再做一次,由于图中的每个顶点最多只能经过一次,那么我们自然想到了把每个点i分成i和i+n两个点. 具体建图如下:
源点s编号0, 所有节点编号1-n和n+1-2*n, 汇点t编号2*n+1.
源点s到第i个点有边 ( s, i, 1, 0)
如果从i点到j点有权值为w的边,那么有边 (i, j+n, 1, w)

每个节点到汇点有边 (i+n, t, 1, 0)

最终如果最大流==n的话,那么最小费用就是我们所求. 否则输-1.

其实任意类似的有向环最小权值覆盖问题都可以用本方法解或用KM算法解.下面说下为什么本建图方法是正确的:

其实就是我们到底要选哪n条不同的边的问题,如果最大流==n的话,我们可以得到4个结论:

1.    我们通过最大流选取了n条不同的(从左边点集到右边点集的类似于(i, j+n)的这种)边

2.    这n条边的起点正好覆盖了n个不同的顶点

3.    这n条边的终点正好覆盖了n个不同的顶点

4.    每个顶点必然是一条边的终点且是另外一条边的起点

最终我们通过最小费用最大流选的这n条边必然组成了1个(或多个)不相交的有向环且费用最小(必要条件,新问题有解->原问题有解)(如果还存在费用更小的有向环,那么我们的算法肯定会找到(充分条件,即原问题有解->新问题有解).

另外,还有一道题就是HDU 4406 GPA 

就是临近考试,你要复习来提高成绩,你有m门课,n天复习,每天只能复习一门课,每门课一天最多复习k次,复习完一次成绩就会加1分,每天只能复习一部分课程。给你每门课的学分以及基础分和每门课的绩点计算公式,GPA的计算公式,求你经过复习后最高能够获得的GPA是多少。如果最终有某一门不够60分,GPA为0,分最高为100分。

想了半天,还是能够想到思路的。建立源点s和汇点t,每一天都建立一条到t的边,容量为k,费用为0,每门课向能复习的那天连容量为k费用为0的边。那么问题的关键就来了:

因为要先保证每门分够60分,所以基础分不够60分的科目,连一条s到这门课的边,容量为60-s,费用为INF,然后添加s到这门课的40条边(因为满分为100,而且每增加一分增加的绩点是不同的),假设分数为p,学分为w,那么这40条边的容量就是1,费用为(f(p+1,w)-f(p,w)),其中f(p,w)=(4.0-3.0*(100-x)*(100-x)/1600)*w;对于基础分够60分的科目,假设基础分为x,那么就做100-x条s到这门课的边,容量为1,费用为(f(p+1,w)-f(p,w))。最后求最大费用最大流,统计s引出的边的流量加上基础分的总分数,还有小于60分的就输出0,否则就算出GPA。。。

前一天:我感觉我的思路对了呀,为啥代码样例都过不了???sad。。。明天找找错。。。应该是细节出了问题吧。。。

今天:原来是有个地方+-号写错了。。改过来秒A。。。

AC code:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=1005;
const int INF=1e9+7;
struct edge{
 int from,to,cap,flow;
 double cost;
 edge(){}
 edge(int f,int t,int c,int fl,double co):from(f),to(t),cap(c),flow(fl),cost(co){}
};
struct MCMF
{
    int n,m,s,t;
    vector<edge> edges;
    vector<int> G[maxn];
    bool inq[maxn];
    double d[maxn];
    int p[maxn],a[maxn];
    void init(int n,int s,int t)
    {
        this->n=n,this->s=s,this->t=t;
        edges.clear();
        for(int i=0;i<n;i++) G[i].clear();
    }
    void add(int u,int v,int c,double cost)
    {
        edges.push_back(edge(u,v,c,0,cost));
        edges.push_back(edge(v,u,0,0,-cost));
        m=edges.size();
        G[u].push_back(m-2);
        G[v].push_back(m-1);
    }
    int mcmf(int s,int t)
    {
     queue<int>q;
     for(int i=0;i<=t;++i)d[i]=(-1.0*INF);
     memset(inq,false,sizeof(inq));
     memset(p,-1,sizeof(p));
     memset(a,0,sizeof(a));
     q.push(s),inq[s]=true;
     d[s]=0,a[s]=INF;
     while(!q.empty())
     {
         int u=q.front();q.pop();
         inq[u]=false;
         for(int i=0;i<G[u].size();++i)
         {
             edge& e=edges[G[u][i]];
             if(e.cap>e.flow&&d[e.to]<d[u]+e.cost)
             {
                 d[e.to]=d[u]+e.cost;
                 a[e.to]=min(a[u],e.cap-e.flow);
                 p[e.to]=G[u][i];
                 if(!inq[e.to]){inq[e.to]=true;q.push(e.to);}
             }
         }
     }
     return a[t];
    }
    int work(int s,int t)
    {
     int minflow,mincost=0;
     while(minflow=mcmf(s,t))
     {
        for(int i=p[t];i!=-1;i=p[edges[i].from])
        {
         mincost+=minflow*edges[i].cost;
         edges[i].flow+=minflow;
         edges[i^1].flow-=minflow;
        }
     }
     return mincost;
    }
}mm;
double cal(int x,int w)
{
return (4.0-3.0*(100-x)*(100-x)/1600)*w;
}
int a[45][45],n,m,k,s[30],p[30];
int main(){
    int T,cas=1,t,S;
      while(scanf("%d%d%d",&n,&k,&m)!=EOF&&(n||m||k))
      {
          S=0;t=n+m+1;
          mm.init(t+1,S,t);
          for(int i=1;i<=m;i++)
          {
              scanf("%d",&p[i]);
          }
          for(int i=1;i<=m;i++)
          {
           scanf("%d",&s[i]);
          }
          for(int i=1;i<=n;i++)mm.add(i+m,t,k,0);
          for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++){
              scanf("%d",&a[i][j]);
              if(a[i][j])mm.add(j,i+m,k,0);
          }
          double now,pre;
          for(int i=1;i<=m;i++)
          {
              if(s[i]<60)
              {
               mm.add(S,i,60-s[i],1.0*INF);
               pre=cal(60,p[i]);
               for(int j=61;j<=100;j++)
               {
                now=cal(j,p[i]);
                mm.add(S,i,1,now-pre);
                pre=now;
               }
              }
              else {
                 pre=cal(s[i],p[i]);
               for(int j=s[i]+1;j<=100;j++)
               {
                now=cal(j,p[i]);
                mm.add(S,i,1,now-pre);
                pre=now;
               }
              }
          }
          mm.work(S,t);
          for(int i=0;i<mm.G[S].size();++i)
         {
             edge e=mm.edges[mm.G[S][i]];
             s[e.to]+=mm.edges[mm.G[S][i]^1].cap-mm.edges[mm.G[S][i]^1].flow;
         }
         int sum=0;
         for(int i=1;i<=m;i++)sum+=p[i];
         double ans=0;
         int i;
         for(i=1;i<=m;i++)
         {
          if(s[i]<60) break;
          ans+=cal(s[i],p[i])/sum;
         }
         if(i<=m) ans=0.0;
         printf("%.6lf\n",ans);
      }
return 0;
}


短暂而又快乐的寒假集训就要结束了。。。然而还是要继续努力。。。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值