【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
//Round4 A
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
inline int getint(){
int r=,v=; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-;
for(; isdigit(ch);ch=getchar()) v=v*-''+ch;
return r*v;
}
const int N=,M=;
const double eps=1e-,INF=1e10;
/*******************template********************/ int n,m,u[M],v[M];
double p[M],ans,c[N];
struct edge{int to;double v;};
struct Net{
edge E[M];
int head[N],nxt[M<<],cnt;
void ins(int x,int y,double v){
E[++cnt]=(edge){y,v}; nxt[cnt]=head[x]; head[x]=cnt;
}
void add(int x,int y,double v){
ins(x,y,v); ins(y,x,);
}
int S,T,cur[N],d[N];
queue<int>Q;
bool mklevel(){
memset(d,-,sizeof d);
d[S]=;
Q.push(S);
while(!Q.empty()){
int x=Q.front(); Q.pop();
for(int i=head[x];i;i=nxt[i])
if (d[E[i].to]==- && E[i].v>){
d[E[i].to]=d[x]+;
Q.push(E[i].to);
}
}
return d[T]!=-;
}
double dfs(int x,double a){
if (x==T) return a;
double flow=0.0;
for(int &i=cur[x];i && a-flow>eps;i=nxt[i]){
if (d[E[i].to]==d[x]+ && E[i].v>eps){
double f=dfs(E[i].to,min(a-flow,E[i].v));
E[i].v-=f;
E[i^].v+=f;
flow+=f;
}
}
if (fabs(flow)<eps) d[x]=-;
return flow;
}
void Dinic(){
while(mklevel()){
F(i,,T) cur[i]=head[i];
ans-=dfs(S,INF);
}
}
void build(double x){
// cout <<"mid="<<x<<endl;
cnt=; memset(head,,sizeof head);
S=,T=n+m+; ans=0.0;
F(i,,n) add(S,i,(double)c[i]*x);
F(i,,m){
ans+=p[i];
add(u[i],i+n,INF);
add(v[i],i+n,INF);
add(i+n,T,p[i]);
}
}
void init(){
n=getint(); m=getint();
F(i,,n) c[i]=getint();
F(i,,m){
u[i]=getint(); v[i]=getint(); p[i]=getint();
}
double l=,r=1e9,mid;
while(r-l>eps){
mid=(l+r)/;
build(mid);
Dinic();
// printf("mid=%f ans=%f\n",mid,ans);
if (ans<eps) r=mid;
else l=mid;
}
printf("%.2f\n",l);
}
}G1; int main(){
#ifndef ONLINE_JUDGE
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
#endif
G1.init();
return ;
}
B
更神的状压DP……求包括所有点的,边权和最小的边双?。。。
$n\leq 12 ,m\leq 50$,很容易猜到n是指数级……枚举= =而m是多重循环的(由于前面有指数级的枚举,这里大约应该是两重循环吧)
然而并不会2333
看题解&标程:
核心思想是:一个边双可以通过从一个边双(点)开始,不断加链……不断加链得到!
所以就DP枚举啦~令$f[i]$表示点集$i$为一个边双的最小代价,那么枚举$i$的一个子集,令其为一个边双,然后剩余部分组成一条链,加入到这个边双中。
预处理出来:
1.点集S组成一条链,且两端点为x、y的最小边权和
2.点x连向点集S的最小边权和次小边权(用来将(链/点)连入到边双中
//Round 4 B
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
using namespace std;
typedef long long LL;
inline int getint(){
int r=,v=; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-;
for(; isdigit(ch);ch=getchar()) v=v*-''+ch;
return r*v;
}
const int N=,INF=<<;
#define CC(a,b) memset(a,b,sizeof(a))
/*******************template********************/ int n,m,bin[N],g[N][][],h[N][][];
int f[N];
struct edge{int x,y,c,next;}E[N];
int head[],cnt;
void add(int x,int y,int c){
E[++cnt]=(edge){x,y,c,head[x]}; head[x]=cnt;
E[++cnt]=(edge){y,x,c,head[y]}; head[y]=cnt;
} inline void getmin(int &a,int b){a=min(a,b);}
inline int fac(int x){
int ans=;
for(x=x&(x-);x;x=x&(x-)) ans++;
return ans;
}
void init(){
m=(<<n)-;
F(i,,m) F(j,,n) F(k,,n) g[i][j][k]=INF;
F(i,,n){
bin[i]=<<(i-);
g[bin[i]][i][i]=;
}
F(i,,cnt){
int x=E[i].x,y=E[i].y,s=bin[x]+bin[y];
g[s][x][y]=min(g[s][x][y],E[i].c);//直接相邻的两点
} F(i,,m)
F(x,,n) F(y,,n)
if ((bin[x]|i)==i && (bin[y]|i)==i)
for(int j=head[y];j;j=E[j].next)
if ((bin[E[j].y]|i)!=i)
getmin(g[i|bin[E[j].y]][x][E[j].y],g[i][x][y]+E[j].c);
//从链的一端向前扩展 F(i,,m) F(j,,n) F(k,,) h[i][j][k]=INF;
F(i,,m)
F(x,,n)
if ((bin[x]|i)!=i)//找个在点集i之外的点x
for(int j=head[x];j;j=E[j].next)
if ((bin[E[j].y]|i)==i){
if (E[j].c<=h[i][x][]){
h[i][x][]=h[i][x][];
h[i][x][]=E[j].c;
}else getmin(h[i][x][],E[j].c);
}//更新从x到i的最短&次短边权
}
void work(){
F(i,,m) f[i]=INF;
F(i,,n) f[bin[i]]=; F(i,,m)
if (fac(i)>=)//元素个数大于2
for(int s=i&(i-);s;s=i&(s-)){
int t=i-s;
F(x,,n) F(y,,n)
if ((bin[x]|s)==s && (bin[y]|s)==s){
if (x==y)
getmin(f[i],f[t]+g[s][x][x]+h[t][x][]+h[t][x][]);
else
getmin(f[i],f[t]+g[s][x][y]+h[t][x][]+h[t][y][]);
}
}
if (f[m]<INF) printf("%d\n",f[m]);
else puts("Impossible");
}
int main(){
#ifndef ONLINE_JUDGE
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
#endif
int T=getint();
while(T--){
n=getint(); m=getint();
cnt=; CC(head,);
F(i,,m){
int x=getint(),y=getint(),c=getint();
add(x,y,c);
}
init(); work();
}
return ;
}
C
……这个……不想说什么了……其实是【TYVJ 五月图论专项有奖比赛】的C题,而且数据规模还更小了……
然而我个傻逼还是不会做QAQ,直接贴了原来的代码>_>原谅我……
//Round 4 C
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
inline int getint(){
int r=,v=; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-;
for(; isdigit(ch);ch=getchar()) v=v*-''+ch;
return r*v;
}
const int N=;
/*******************template********************/
int to[N<<],next[N<<],head[N],cnt;
void add(int x,int y){
to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt;
to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt;
} int n,a[N],f[N][],dep[N],s[N],fa[N],cut;
LL g[N],ans=1e15;
void dfs(int x){
for(int i=head[x];i;i=next[i])
if (to[i]!=fa[x]){
dep[to[i]]=dep[x]+;
fa[to[i]]=x;
dfs(to[i]);
s[x]+=s[to[i]];
if (s[to[i]]>s[f[x][]]) f[x][]=f[x][],f[x][]=to[i];
else if (s[to[i]]>s[f[x][]]) f[x][]=to[i];
g[x]+=g[to[i]]+s[to[i]];
}
}
LL getans(int x,int cnt){
int t=(f[x][]==cut || s[f[x][]]>s[f[x][]]) ? f[x][] : f[x][];
if (*s[t]>cnt) return *s[t]-cnt+getans(t,cnt);
else return ;
}
void dp(int x){
for(int i=head[x];i;i=next[i])
if (to[i]!=fa[x]){
cut=to[i];
for(int j=x;j;j=fa[j]) s[j]-=s[to[i]];
ans=min(ans,g[]-g[to[i]]-(LL)s[to[i]]*dep[to[i]]-getans(,s[])+g[to[i]]-getans(to[i],s[to[i]]));
for(int j=x;j;j=fa[j]) s[j]+=s[to[i]];
dp(to[i]);
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
#endif
n=getint();
F(i,,n){
int x=getint(),y=getint();
add(x,y);
}
F(i,,n) s[i]=getint();
dfs();
dp();
printf("%lld\n",ans);
return ;
}
【ContestHunter】【弱省胡策】【Round4】的更多相关文章
- 弱省胡策 Magic
弱省胡策 Magic 求\(n\)个点\(n\)的条边的简单联通图的个数. 毒瘤,还要写高精. 我们枚举环的大小\(k\),\(\displaystyle ans=\sum_{k=3}^nC_n^k ...
- 【弱省胡策】Round #5 Count
[弱省胡策]Round #5 Count 太神仙了. \(DP\)做法 设\(f_{n,m,d,k}\)表示\(n*m\)的矩阵,填入第\(k\)个颜色,并且第\(k\)个颜色最少的一列上有\(d\) ...
- luoguP3769 [CH弱省胡策R2]TATT
luoguP3769 [CH弱省胡策R2]TATT PS:做这题前先切掉 P4148简单题,对于本人这样的juruo更助于理解,当然dalao就当练练手吧 题目大意: 现在有n个四维空间中的点,请求出 ...
- 【ContestHunter】【弱省胡策】【Round0】(A)&【Round1】(B)
DP+容斥原理or补集转化?/KD-Tree 唔……突然发现最早打的两场(打的最烂的两场)没有写记录……(太烂所以不忍记录了吗... 还是把搞出来了的两道题记录一下吧= =勉强算弥补一下缺憾…… Ro ...
- 【ContestHunter】【弱省胡策】【Round3】(C)
容斥原理+Fib Orz HE的神犇们 蒟蒻只能改出来第三题……实在太弱 官方题解:http://pan.baidu.com/s/1o6MdtQq fib的神奇性质……还有解密a[i]的过程……这里就 ...
- 【ContestHunter】【弱省胡策】【Round2】
官方题解:http://wyfcyx.is-programmer.com/posts/95490.html A 目前只会30分的暴力……DP好像很神的样子0.0(听说可以多次随机强行算? //Roun ...
- 【ContestHunter】【弱省胡策】【Round8】
平衡树维护凸壳/三角函数+递推+线段树 官方题解:http://pan.baidu.com/s/1sjQbY8H 洛阳城里春光好 题目大意:(其实出题人已经写的很简短了……直接copy的-_-.sor ...
- 【ContestHunter】【弱省胡策】【Round7】
Prufer序列+高精度+组合数学/DP+可持久化线段树 Magic 利用Prufer序列,我们考虑序列中每个点是第几个插进去的,再考虑环的连接方式,我们有$$ans=\sum_{K=3}^n N^{ ...
- 【ContestHunter】【弱省胡策】【Round6】
KMP/DP+树链剖分+线段树/暴力 今天考的真是……大起大落…… String QwQ题意理解又出错了……(还是说一开始理解了,后来自己又忘了为什么是这样了?) 反正最后的结果就是……我当成:后面每 ...
随机推荐
- Ionic Js九:列表操作
列表是一个应用广泛在几乎所有移动app中的界面元素.ionList 和 ionItem 这两个指令还支持多种多样的交互模式,比如移除其中的某一项,拖动重新排序,滑动编辑等等. <ion-list ...
- TCP可靠传输及流量控制实现原理
一.为什么TCP是可靠传输? 1. 停止等待协议 通过确认与超时重传机制实现可靠传输 在发送完一个分组后,必须暂时保留已发送的分组的副本. 分组和确认分组都必须进行编号. 超时计时器的重传时间应当比数 ...
- 美团外卖iOS多端复用的推动、支撑与思考
背景 美团外卖2013年11月开始起步,随后高速发展,不断刷新多项行业记录.截止至2018年5月19日,日订单量峰值已超过2000万,是全球规模最大的外卖平台.业务的快速发展对技术支撑提出了更高的要求 ...
- JDK源码分析(三)——HashMap 下(基于JDK8)
目录 概述 内部字段及构造方法 哈希值与索引计算 存储元素 扩容 删除元素 查找元素 总结 概述 在上文我们基于JDK7分析了HashMap的实现源码,介绍了HashMap的加载因子loadFac ...
- python 发送邮件(收到的邮件要有发送方才能回复)
Python使用SMTP(简单邮件传输协议)发送邮件 普通文本邮件 普通文本邮件发送的实现,关键是要将MIMEText中_subtype设置为plain ## -*- coding: UTF-8 -* ...
- 【洛谷】4304:[TJOI2013]攻击装置【最大点独立集】【二分图】2172: [国家集训队]部落战争【二分图/网络流】【最小路径覆盖】
P4304 [TJOI2013]攻击装置 题目描述 给定一个01矩阵,其中你可以在0的位置放置攻击装置. 每一个攻击装置(x,y)都可以按照“日”字攻击其周围的8个位置(x-1,y-2),(x-2,y ...
- 【失踪人口回归】第11届东北地区大学生程序设计竞赛——Time to make some change
对哈尔滨出租车和纸质题目和2148473647的吐槽都被毕克神牛在知乎上(https://www.zhihu.com/question/59782275/answer/169402588)pick/b ...
- 监听当点击微信等app的返回按钮或者浏览器的上一页或后退按钮的事件
在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...
- Python学习笔记(七)—字典的学习
总结内容: 1.字典的定义 2.字典的好处 3.字典的增删改查 4.字典常用方法及内置函数 5.字典的多层嵌套 6.字典的循环 7.字典小练习 1.字典的定义 字典是另一种可变容器模型,且可存储任意类 ...
- IO流-递归遍历目录下指定后缀名结尾的文件名称
/* *自定义遍历目录下指定后缀名结尾文件的名称的方法: * * param file:指定目录 name:指定后缀名 */ 1 public static void FileName(File fi ...