【ContestHunter】【弱省胡策】【Round4】
01分数规划(网络流)+状压DP+树形DP
官方题解地址:http://pan.baidu.com/s/1mg5S5z6
A
好神啊= =第一次写01分数规划
其实分数规划是要求$$ Maximize/Minimize \ \ L=\frac{A(x)}{B(x)}$$
这里我们拿最大来举例吧……因为本题就是最大嘛~
通用解法是:二分= =
假设最优解为$\lambda^*$,那么有$$\lambda^*=f(x^*)=\frac{A(x^*)}{B(x^*)} \\ \Rightarrow \lambda^* *B(x^*)=A(x^*) \\ \Rightarrow 0=A(x^*)-\lambda^* *B(x^*) $$
那么我们由上面的形式构造一个新函数$g(\lambda)$:$$g(\lambda)=max_{x \in S} \{A(x)-\lambda*B(x)\}$$
这个函数是有单调性的……我就不证明了……重点是后面的部分:
$$ \lambda<\lambda^* \Rightarrow \lambda<\frac{A(x^*)}{B(x^*)} \Rightarrow A(x^*)-\lambda *B(x^*) >0 \\ \lambda>\lambda^* \Rightarrow \lambda>\frac{A(x^*)}{B(x^*)} \Rightarrow A(x^*)-\lambda *B(x^*) <0 $$
所以我们得到这样一个定理(重要):$$\begin{cases} g( \lambda )=0 & \Leftrightarrow & \lambda = \lambda^* \\ g( \lambda )<0 & \Leftrightarrow & \lambda < \lambda^* \\ g( \lambda )>0 & \Leftrightarrow & \lambda > \lambda^* \end{cases} $$
这题里面,$A(x)$就是边权和,$\lambda*B(x)$就是选这些点的代价啦。
那么很明显对于一个已知的$\lambda$(收益比),我们可以用最大权闭合图的模型来求出$g(\lambda)$,(因为是最大!)从而得知我们二分的这个答案$\lambda$是偏大还是偏小了……
这玩意是不是叫点边均带权的最大密度子图啊QAQ
然而这题有个细节要注意,因为我们用的是最大权闭合图,这里的边权又比较特殊……所以不会出现$g(\lambda)<0$的情况,(那种时候会变成=0),所以我们应该是在>0的时候令L=mid;else R=mid;
而我一开始是反过来写的……所以就跪了TAT 后来我灵(nao)机(zi)一(yi)动(chou),将ans<0改成了ans<eps……这就将<=0的情况都包括进去了→_→顺利出解。
事实上考试时我这题只有暴力分= =因为我数组开!小!了!……一开始定数组大小的时候没想出来正解……所以是直接按点数和边数开的……但是如果是网络流的话,点数应该是$n+m$,边数会是$n+3m$……(没算源汇)QAQTAT
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //Round4 A 2 #include<cstdio> 3 #include<cmath> 4 #include<queue> 5 #include<cstring> 6 #include<cstdlib> 7 #include<iostream> 8 #include<algorithm> 9 #define rep(i,n) for(int i=0;i<n;++i) 10 #define F(i,j,n) for(int i=j;i<=n;++i) 11 #define D(i,j,n) for(int i=j;i>=n;--i) 12 #define pb push_back 13 using namespace std; 14 typedef long long LL; 15 inline int getint(){ 16 int r=1,v=0; char ch=getchar(); 17 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 18 for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch; 19 return r*v; 20 } 21 const int N=10010,M=40010; 22 const double eps=1e-6,INF=1e10; 23 /*******************template********************/ 24 25 int n,m,u[M],v[M]; 26 double p[M],ans,c[N]; 27 struct edge{int to;double v;}; 28 struct Net{ 29 edge E[M]; 30 int head[N],nxt[M<<1],cnt; 31 void ins(int x,int y,double v){ 32 E[++cnt]=(edge){y,v}; nxt[cnt]=head[x]; head[x]=cnt; 33 } 34 void add(int x,int y,double v){ 35 ins(x,y,v); ins(y,x,0); 36 } 37 int S,T,cur[N],d[N]; 38 queue<int>Q; 39 bool mklevel(){ 40 memset(d,-1,sizeof d); 41 d[S]=0; 42 Q.push(S); 43 while(!Q.empty()){ 44 int x=Q.front(); Q.pop(); 45 for(int i=head[x];i;i=nxt[i]) 46 if (d[E[i].to]==-1 && E[i].v>0){ 47 d[E[i].to]=d[x]+1; 48 Q.push(E[i].to); 49 } 50 } 51 return d[T]!=-1; 52 } 53 double dfs(int x,double a){ 54 if (x==T) return a; 55 double flow=0.0; 56 for(int &i=cur[x];i && a-flow>eps;i=nxt[i]){ 57 if (d[E[i].to]==d[x]+1 && E[i].v>eps){ 58 double f=dfs(E[i].to,min(a-flow,E[i].v)); 59 E[i].v-=f; 60 E[i^1].v+=f; 61 flow+=f; 62 } 63 } 64 if (fabs(flow)<eps) d[x]=-1; 65 return flow; 66 } 67 void Dinic(){ 68 while(mklevel()){ 69 F(i,0,T) cur[i]=head[i]; 70 ans-=dfs(S,INF); 71 } 72 } 73 void build(double x){ 74 // cout <<"mid="<<x<<endl; 75 cnt=1; memset(head,0,sizeof head); 76 S=0,T=n+m+1; ans=0.0; 77 F(i,1,n) add(S,i,(double)c[i]*x); 78 F(i,1,m){ 79 ans+=p[i]; 80 add(u[i],i+n,INF); 81 add(v[i],i+n,INF); 82 add(i+n,T,p[i]); 83 } 84 } 85 void init(){ 86 n=getint(); m=getint(); 87 F(i,1,n) c[i]=getint(); 88 F(i,1,m){ 89 u[i]=getint(); v[i]=getint(); p[i]=getint(); 90 } 91 double l=0,r=1e9,mid; 92 while(r-l>eps){ 93 mid=(l+r)/2; 94 build(mid); 95 Dinic(); 96 // printf("mid=%f ans=%f\n",mid,ans); 97 if (ans<eps) r=mid; 98 else l=mid; 99 } 100 printf("%.2f\n",l); 101 } 102 }G1; 103 104 int main(){ 105 #ifndef ONLINE_JUDGE 106 freopen("A.in","r",stdin); 107 freopen("A.out","w",stdout); 108 #endif 109 G1.init(); 110 return 0; 111 }
B
更神的状压DP……求包括所有点的,边权和最小的边双?。。。
$n\leq 12 ,m\leq 50$,很容易猜到n是指数级……枚举= =而m是多重循环的(由于前面有指数级的枚举,这里大约应该是两重循环吧)
然而并不会2333
看题解&标程:
核心思想是:一个边双可以通过从一个边双(点)开始,不断加链……不断加链得到!
所以就DP枚举啦~令$f[i]$表示点集$i$为一个边双的最小代价,那么枚举$i$的一个子集,令其为一个边双,然后剩余部分组成一条链,加入到这个边双中。
预处理出来:
1.点集S组成一条链,且两端点为x、y的最小边权和
2.点x连向点集S的最小边权和次小边权(用来将(链/点)连入到边双中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //Round 4 B 2 #include<vector> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<iostream> 7 #include<algorithm> 8 #define rep(i,n) for(int i=0;i<n;++i) 9 #define F(i,j,n) for(int i=j;i<=n;++i) 10 #define D(i,j,n) for(int i=j;i>=n;--i) 11 using namespace std; 12 typedef long long LL; 13 inline int getint(){ 14 int r=1,v=0; char ch=getchar(); 15 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 16 for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch; 17 return r*v; 18 } 19 const int N=10100,INF=1<<28; 20 #define CC(a,b) memset(a,b,sizeof(a)) 21 /*******************template********************/ 22 23 24 int n,m,bin[N],g[N][15][15],h[N][15][2]; 25 int f[N]; 26 struct edge{int x,y,c,next;}E[N]; 27 int head[15],cnt; 28 void add(int x,int y,int c){ 29 E[++cnt]=(edge){x,y,c,head[x]}; head[x]=cnt; 30 E[++cnt]=(edge){y,x,c,head[y]}; head[y]=cnt; 31 } 32 33 inline void getmin(int &a,int b){a=min(a,b);} 34 inline int fac(int x){ 35 int ans=1; 36 for(x=x&(x-1);x;x=x&(x-1)) ans++; 37 return ans; 38 } 39 void init(){ 40 m=(1<<n)-1; 41 F(i,0,m) F(j,0,n) F(k,0,n) g[i][j][k]=INF; 42 F(i,1,n){ 43 bin[i]=1<<(i-1); 44 g[bin[i]][i][i]=0; 45 } 46 F(i,1,cnt){ 47 int x=E[i].x,y=E[i].y,s=bin[x]+bin[y]; 48 g[s][x][y]=min(g[s][x][y],E[i].c);//直接相邻的两点 49 } 50 51 F(i,1,m) 52 F(x,1,n) F(y,1,n) 53 if ((bin[x]|i)==i && (bin[y]|i)==i) 54 for(int j=head[y];j;j=E[j].next) 55 if ((bin[E[j].y]|i)!=i) 56 getmin(g[i|bin[E[j].y]][x][E[j].y],g[i][x][y]+E[j].c); 57 //从链的一端向前扩展 58 59 F(i,0,m) F(j,0,n) F(k,0,1) h[i][j][k]=INF; 60 F(i,1,m) 61 F(x,1,n) 62 if ((bin[x]|i)!=i)//找个在点集i之外的点x 63 for(int j=head[x];j;j=E[j].next) 64 if ((bin[E[j].y]|i)==i){ 65 if (E[j].c<=h[i][x][0]){ 66 h[i][x][1]=h[i][x][0]; 67 h[i][x][0]=E[j].c; 68 }else getmin(h[i][x][1],E[j].c); 69 }//更新从x到i的最短&次短边权 70 } 71 void work(){ 72 F(i,1,m) f[i]=INF; 73 F(i,1,n) f[bin[i]]=0; 74 75 F(i,1,m) 76 if (fac(i)>=2)//元素个数大于2 77 for(int s=i&(i-1);s;s=i&(s-1)){ 78 int t=i-s; 79 F(x,1,n) F(y,1,n) 80 if ((bin[x]|s)==s && (bin[y]|s)==s){ 81 if (x==y) 82 getmin(f[i],f[t]+g[s][x][x]+h[t][x][0]+h[t][x][1]); 83 else 84 getmin(f[i],f[t]+g[s][x][y]+h[t][x][0]+h[t][y][0]); 85 } 86 } 87 if (f[m]<INF) printf("%d\n",f[m]); 88 else puts("Impossible"); 89 } 90 int main(){ 91 #ifndef ONLINE_JUDGE 92 freopen("B.in","r",stdin); 93 freopen("B.out","w",stdout); 94 #endif 95 int T=getint(); 96 while(T--){ 97 n=getint(); m=getint(); 98 cnt=0; CC(head,0); 99 F(i,1,m){ 100 int x=getint(),y=getint(),c=getint(); 101 add(x,y,c); 102 } 103 init(); work(); 104 } 105 return 0; 106 }
C
……这个……不想说什么了……其实是【TYVJ 五月图论专项有奖比赛】的C题,而且数据规模还更小了……
然而我个傻逼还是不会做QAQ,直接贴了原来的代码>_>原谅我……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //Round 4 C 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;++i) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 #define pb push_back 11 using namespace std; 12 typedef long long LL; 13 inline int getint(){ 14 int r=1,v=0; char ch=getchar(); 15 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 16 for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch; 17 return r*v; 18 } 19 const int N=100010; 20 /*******************template********************/ 21 int to[N<<1],next[N<<1],head[N],cnt; 22 void add(int x,int y){ 23 to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt; 24 to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt; 25 } 26 27 int n,a[N],f[N][2],dep[N],s[N],fa[N],cut; 28 LL g[N],ans=1e15; 29 void dfs(int x){ 30 for(int i=head[x];i;i=next[i]) 31 if (to[i]!=fa[x]){ 32 dep[to[i]]=dep[x]+1; 33 fa[to[i]]=x; 34 dfs(to[i]); 35 s[x]+=s[to[i]]; 36 if (s[to[i]]>s[f[x][0]]) f[x][1]=f[x][0],f[x][0]=to[i]; 37 else if (s[to[i]]>s[f[x][1]]) f[x][1]=to[i]; 38 g[x]+=g[to[i]]+s[to[i]]; 39 } 40 } 41 LL getans(int x,int cnt){ 42 int t=(f[x][0]==cut || s[f[x][1]]>s[f[x][0]]) ? f[x][1] : f[x][0]; 43 if (2*s[t]>cnt) return 2*s[t]-cnt+getans(t,cnt); 44 else return 0; 45 } 46 void dp(int x){ 47 for(int i=head[x];i;i=next[i]) 48 if (to[i]!=fa[x]){ 49 cut=to[i]; 50 for(int j=x;j;j=fa[j]) s[j]-=s[to[i]]; 51 ans=min(ans,g[1]-g[to[i]]-(LL)s[to[i]]*dep[to[i]]-getans(1,s[1])+g[to[i]]-getans(to[i],s[to[i]])); 52 for(int j=x;j;j=fa[j]) s[j]+=s[to[i]]; 53 dp(to[i]); 54 } 55 } 56 57 int main(){ 58 #ifndef ONLINE_JUDGE 59 freopen("C.in","r",stdin); 60 freopen("C.out","w",stdout); 61 #endif 62 n=getint(); 63 F(i,2,n){ 64 int x=getint(),y=getint(); 65 add(x,y); 66 } 67 F(i,1,n) s[i]=getint(); 68 dfs(1); 69 dp(1); 70 printf("%lld\n",ans); 71 return 0; 72 }