明明思路对了,却是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;
}
短暂而又快乐的寒假集训就要结束了。。。然而还是要继续努力。。。