【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

  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 }
View Code

 

B

  更神的状压DP……求包括所有点的,边权和最小的边双?。。。

  $n\leq 12 ,m\leq 50$,很容易猜到n是指数级……枚举= =而m是多重循环的(由于前面有指数级的枚举,这里大约应该是两重循环吧)

  然而并不会2333

  看题解&标程:

  核心思想是:一个边双可以通过从一个边双(点)开始,不断加链……不断加链得到!

  所以就DP枚举啦~令$f[i]$表示点集$i$为一个边双的最小代价,那么枚举$i$的一个子集,令其为一个边双,然后剩余部分组成一条链,加入到这个边双中。

  预处理出来:

    1.点集S组成一条链,且两端点为x、y的最小边权和

    2.点x连向点集S的最小边权和次小边权(用来将(链/点)连入到边双中

  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 }
View Code

 

C

  ……这个……不想说什么了……其实是【TYVJ 五月图论专项有奖比赛】的C题,而且数据规模还更小了……

  然而我个傻逼还是不会做QAQ,直接贴了原来的代码>_>原谅我……

 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 }
View Code

 

  

posted @ 2015-06-10 09:24  Tunix  阅读(...)  评论(...编辑  收藏