一道图论好题(graph)

Time Limit:1000ms   Memory Limit:128MB

题目描述

LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,不仅有边权还有点权。

LYK给出了一个子图的定义,一张图G’={V’,E’}被称作G的子图,当且仅当

·G’的点集V’包含于G的点集V。

·对于E中的任意两个点a,b∈V’,当(a,b)∈E时,(a,b)一定也属于E’,并且连接这两个点的边的边权是一样的。

LYK给一个子图定义了它的价值,它的价值为:点权之和与边权之和的比。 看

LYK想找到一个价值最大的非空子图,所以它来找你帮忙啦。

输入格式(graph.in)

第一行两个数n,m表示一张n个点m条边的图。

第二行n个数ai表示点权。

接下来m行每行三个数u,v,z,表示有一条连接u,v的边权为z的无向边。数据保证任意两个点之间最多一条边相连,并且不存在自环。

输出格式(graph.out)

你需要输出这个价值最大的非空子图的价值,由于它是一个浮点数,你只需要保留小数点后两位有效数字。

输入样例

3 3

2 3 4

1 2 3

1 3 4

2 3 5

输出样例

1.67

样例解释

选择1,2两个点,则价值为5/3=1.67。

对于20%的数据n=2

对于50%的数据n<=5

对于100%的数据1<=n,m<=100000,1<=ai,z<=1000。

思路:当时跑了01分数规划,但是用的bfs找的负环,所以TLE,卡了我5个点。

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 100010
using namespace std;
int n,m,tot;
double w[MAXN],dis[MAXN];
double l=,r=,ans,mid;
int val[MAXN],vis[MAXN],num[MAXN];
int to[MAXN*],net[MAXN*],cap[MAXN*],head[MAXN*];
void add(int u,int v,int w){
to[++tot]=v;net[tot]=head[u];cap[tot]=w;head[u]=tot;
to[++tot]=u;net[tot]=head[v];cap[tot]=w;head[v]=tot;
}
bool spfa(int s){
queue<int>que;
memset(num,,sizeof(num));
memset(vis,,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
que.push(s);
dis[s]=;vis[s]=;num[s]++;
while(!que.empty()){
int now=que.front();
que.pop();
vis[now]=;
for(int i=head[now];i;i=net[i]){
if(dis[to[i]]>dis[now]+w[i]){
dis[to[i]]=dis[now]+w[i];
if(!vis[to[i]]){
vis[to[i]]=;
num[to[i]]++;
que.push(to[i]);
if(num[to[i]]>n) return true;
}
}
}
}
return false;
}
void work(){
for(int i=;i<=tot;i++)
w[i]=(double)cap[i]*mid-val[to[i]];
}
bool check(){
for(int i=;i<=n;i++)
if(spfa(i)) return true;
return false;
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&val[i]);
for(int i=;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
while(r-l>0.0000001){
mid=(l+r)/;
work();
if(check()){
ans=mid;
l=mid;
}
else r=mid;
}
printf("%.2lf",ans*);
}

然后改成dfs找负环,AC了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const double eps=1e-;
const int mxn=;
int read(){
int x=,f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>='' && ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
struct edge{
int v,nxt,w;
double c;
}e[mxn<<];
int hd[mxn*],mct=;
void add_edge(int u,int v,int w){
e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;e[mct].w=w;return;
}
bool vis[mxn];
double dis[mxn];
bool SPFA(int u){
vis[u]=;
for(int i=hd[u];i;i=e[i].nxt){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].c){
dis[v]=dis[u]+e[i].c;
if(vis[v] || SPFA(v)){
vis[v]=;return ;
}
}
}
vis[u]=;
return ;
}
int n,m;
int f[mxn];
void restore(double r){
for(int i=;i<=mct;i++)
e[i].c=(double)e[i].w*r-f[e[i].v];
return;
}
bool check(){
for(int i=;i<=n;i++)
if(SPFA(i))return ;
return ;
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
int i,j;
int u,v,w;
n=read();m=read();
for(i=;i<=n;i++)
f[i]=read();
for(i=;i<=m;i++){
u=read();v=read();w=read();
add_edge(u,v,w);
add_edge(v,u,w);
}
double l=,r=,ans;
while(r-l>eps){
double mid=(l+r)/;
restore(mid);
if(check()){
ans=mid;
l=mid;
}else r=mid;
}
printf("%.2f\n",ans*);
return ;
}

后来听老师讲了之后发现这是道结论题:最优解一定是一条边+两个点。

#include <cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
double ans;
int A,B,C,n,m;
int a[];
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
for(int i=;i<=m;i++){
scanf("%d%d%d",&A,&B,&C);
ans=max(ans,(a[A]+a[B])/(C+0.0));
}
printf("%.2f\n",ans);
return ;
}

拍照(photo)

Time Limit:1000ms   Memory Limit:128MB

题目描述

假设这是一个二次元。

LYK召集了n个小伙伴一起来拍照。他们分别有自己的身高Hi和宽度Wi。

为了放下这个照片并且每个小伙伴都完整的露出来,必须需要一个宽度为ΣWi,长度为max{Hi}的相框。(因为不能叠罗汉)。

LYK为了节省相框的空间,它有了绝妙的idea,让部分人躺着!一个人躺着相当于是身高变成了Wi,宽度变成了Hi。但是很多人躺着不好看,于是LYK规定最多只有n/2个人躺着。(也就是说当n=3时最多只有1个人躺着,当n=4时最多只有2个人躺着)

LYK现在想问你,当其中部分人躺着后,相框的面积最少是多少。

输入格式(photo.in)

第一行一个数n。

接下来n行,每行两个数分别是Wi,Hi。

输出格式(photo.out)

你需要输出这个相框的面积最少是多少。

输入样例

3

3 1

2 2

4 3

输出样例

27

样例解释

如果没人躺过来,需要27的面积。

我们只要让第1个人躺过来,就只需要21的面积!

对于30%的数据n<=10。

对于60%的数据n<=1000,Wi,Hi<=10。

对于100%的数据1<=n,Wi,Hi<=1000。

思路:考试的时候写了个DP,但是忘了考虑他的后效性,所以挂掉了8个点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,sumh,sumw;
int ans=0x7f7f7f7f;
int w[],h[];
int f[][],sum[][];
int maxh[][],maxw[][];
int main(){
freopen("photo.in","r",stdin);
freopen("photo.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d",&w[i],&h[i]);
sumw+=w[i];
sumh+=h[i];
maxh[i][i]=max(w[i],maxh[i-][i-]);
maxh[i][]=max(h[i],maxh[i-][]);
f[i][]=maxh[i][]*sumw;
f[i][i]=maxh[i][i]*sumh;
sum[i][]=sumw;
sum[i][i]=sumh;
}
for(int i=;i<=n;i++)
for(int j=;j<i;j++){
int bns=;
f[i][j]=f[i-][j];
if(h[i]<=maxh[i-][j]){
f[i][j]+=w[i]*maxh[i-][j];
maxh[i][j]=maxh[i-][j];
}
else if(h[i]>maxh[i-][j]){
f[i][j]+=sum[i-][j]*(h[i]-maxh[i-][j])+w[i]*h[i];
maxh[i][j]=h[i];
}
sum[i][j]=sum[i-][j]+w[i]; if(w[i]<=maxh[i-][j-]) bns+=h[i]*maxh[i-][j-];
else if(w[i]>maxh[i-][j-]) bns+=sum[i-][j-]*(w[i]-maxh[i-][j-])+w[i]*h[i];
if(f[i][j]>f[i-][j-]+bns){
f[i][j]=f[i-][j-]+bns;
maxh[i][j]=max(w[i],maxh[i-][j-]);
sum[i][j]=sum[i-][j-]+h[i];
}
}
for(int i=;i<=n/;i++)
ans=min(f[n][i],ans);
cout<<ans;
}

正解思路:枚举高度,贪心累计宽度

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 1005
using namespace std;
int n,ans=0x7f7f7f7f;
int w[MAXN],h[MAXN],bns[MAXN];
int cmp(int a,int b){
return a>b;
}
int main(){
freopen("photo.in","r",stdin);
freopen("photo.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d%d",&w[i],&h[i]);
for(int i=;i<=;i++){
int sum=,flag=,cnt=,num=;
for(int j=;j<=n;j++)
if(h[j]<=i&&(w[j]<h[j]||w[j]>i))
sum+=w[j];
else if(w[j]>i&&h[j]>i){
flag=;
break;
}
else if(h[j]>i){
cnt++;
sum+=h[j];
}
else{
bns[++num]=w[j]-h[j];
sum+=w[j];
}
if(flag) continue;
if(cnt>n/) continue;
sort(bns+,bns++num,cmp);
for(int j=;j<=min(n/-cnt,num);j++)
sum-=bns[j];
ans=min(ans,sum*i);
}
cout<<ans;
}

或和异或(xor)

Time Limit:2000ms   Memory Limit:128MB

题目描述

LYK最近在研究位运算,它研究的主要有两个:or和xor。(C语言中对于|和^)

为了更好的了解这两个运算符,LYK找来了一个2^n长度的数组。它第一次先对所有相邻两个数执行or操作,得到一个2^(n-1)长度的数组。也就是说,如果一开始时a[1],a[2],…,a[2^n],执行完第一次操作后,会得到a[1] or a[2],a[3] or a[4] ,…, a[(2^n)-1] or a[2^n]。

第二次操作,LYK会将所有相邻两个数执行xor操作,得到一个2^(n-2)长度的数组,假如第一次操作后的数组是b[1],b[2],…,b[2^(n-1)],那么执行完这次操作后会变成b[1] xor b[2], b[3] xor b[4] ,…, b[(2^(n-1))-1] xor b[2^(n-1)]。

第三次操作,LYK仍然将执行or操作,第四次LYK执行xor操作。如此交替进行。

最终这2^n个数一定会变成1个数。LYK想知道最终这个数是多少。

为了让这个游戏更好玩,LYK还会执行Q次修改操作。每次修改原先的2^n长度的数组中的某一个数,对于每次修改操作,你需要输出n次操作后(最后一定只剩下唯一一个数)剩下的那个数是多少。

输入格式(xor.in)

第一行两个数n,Q。

接下来一行2^n个数ai表示一开始的数组。

接下来Q行,每行两个数xi,yi,表示LYK这次的修改操作是将a{xi}改成yi。

输出格式(xor.out)

Q行,表示每次修改操作后执行n次操作后剩下的那个数的值。

输入样例

2 4

1 6 3 5

1 4

3 4

1 2

1 2

输出样例

1

3

3

3

样例解释

第一次修改,{4,6,3,5}->{6,7}->{1}

第二次修改,{4,6,4,5}->{6,5}->{3}

第三次修改,{2,6,4,5}->{6,5}->{3}

第四次修改,{2,6,4,5}->{6,5}->{3}

对于30%的数据n<=17,Q=1。

对于另外20%的数据n<=10,Q<=1000。

对于再另外30%的数据n<=12,Q<=100000。

对于100%的数据1<=n<=17,1<=Q<=10^5,1<=xi<=2^n,0<=yi<2^30,0<=ai<2^30。

思路:倍增。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
bool ok;
int a[][];
int poss,v;
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
scanf("%d%d",&n,&m);
int len=pow(,n);
for(int i=;i<=len;i++) scanf("%d",&a[n][i]);
for(int i=n-;i>=;i--){
int l=pow(,i);
if((n-i)%)
for(int j=;j<=l;j++)
a[i][j]=a[i+][(j<<)-]|a[i+][(j<<)];
else
for(int j=;j<=l;j++)
a[i][j]=a[i+][(j<<)-]^a[i+][(j<<)];
}
for(int z=;z<=m;z++){
ok=false;int val,pos;
scanf("%d%d",&pos,&val);
a[n][pos]=val;
if(pos%) poss=pos+;
else{
poss=pos;
pos--;
}
for(int i=n-;i>=;i--)
if((n-i)%){
v=a[i+][pos]|a[i+][poss];
if(v==a[i][(pos>>)+(pos-((pos>>)<<))]){
printf("%d\n",a[][]);
ok=true;
break;
}
a[i][(pos>>)+(pos-((pos>>)<<))]=v;
pos=(pos>>)+(pos-((pos>>)<<));
if(pos%) poss=pos+;
else{
poss=pos;
pos--;
}
}
else{
v=a[i+][pos]^a[i+][poss];
if(v==a[i][(pos>>)+(pos-((pos>>)<<))]){
printf("%d\n",a[][]);
ok=true;
break;
}
a[i][(pos>>)+(pos-((pos>>)<<))]=v;
pos=(pos>>)+(pos-((pos>>)<<));
if(pos%) poss=pos+;
else{
poss=pos;
pos--;
}
}
if(!ok) printf("%d\n",a[][]);
}
}

另一种思路:线段树。

#include<cstdio>
#include<iostream>
#define N 131073
using namespace std;
int n,m;
int sum[N<<],mid[N<<];
void build(int now,int l,int r,int dep){
if(l==r){
scanf("%d",&sum[now]);
return;
}
mid[now]=(l+r)/;
build(now*,l,mid[now],dep+);
build(now*+,mid[now]+,r,dep+);
if((n-dep+)&) sum[now]=(sum[now*]|sum[now*+]);
else sum[now]=(sum[now*]^sum[now*+]);
}
void change(int now,int l,int r,int pos,int w,int dep){
if(l==r){
sum[now]=w;
return;
}
if(pos<=mid[now]) change(now*,l,mid[now],pos,w,dep+);
else change(now*+,mid[now]+,r,pos,w,dep+);
if((n-dep+)&) sum[now]=(sum[now*]|sum[now*+]);
else sum[now]=(sum[now*]^sum[now*+]);
}
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
scanf("%d%d",&n,&m);
int s=<<n;
build(,,s,);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
change(,,s,x,y,);
printf("%d\n",sum[]);
}
}

国庆 day 1 下午的更多相关文章

  1. 国庆 day 7 下午

    思路:见博客. #include<iostream> #include<cstdio> #include<cstring> #include<algorith ...

  2. 国庆 day 2 下午

    最大值(max) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有一本书,上面有很多有趣的OI问题.今天LYK看到了这么一道题目: 这里有一个长度为n的 ...

  3. 国庆 day 6 下午

    1.数组异或 (xorarray.pas/c/cpp) (xorarray.in/out) 时间限制:2s/空间限制:256M [题目描述] xor——异或,和 and 与or 一样,是一种重要的逻辑 ...

  4. 国庆 day 3 下午

    a[问题描述] 你是能看到第一题的 friends 呢. ——hja 给你一个只有小括号和中括号和大括号的括号序列,问该序列是否合法.[输入格式] 一行一个括号序列.[输出格式] 如果合法,输出 OK ...

  5. 2018国庆YALI集训游记

    想了想,像之前那样简略地叙述题意和做法,根本没讲清楚,没有任何意义,还不如写写自己的感受. 感觉YALI真的是一所挺不错的学校吧.总是能有一机房的julao轮番吊打你,总是能有集训队的奆佬来给你出dl ...

  6. 2016.10.4初中部下午NOIP普及组比赛总结

    2016.10.4初中部下午NOIP普及组比赛总结 这次的题有些水,只是第四题有点坑. 题目: 比赛:AC+0+AC+50=250 改题:AC+AC+AC+50=350 一.Bill 满地都是水 题目 ...

  7. 票房和口碑称霸国庆档,用 Python 爬取猫眼评论区看看电影《我和我的家乡》到底有多牛

    今年的国庆档电影市场的表现还是比较强势的,两名主力<我和我的家乡>和<姜子牙>起到了很好的带头作用. <姜子牙>首日破 2 亿,一举刷新由<哪吒之魔童降世&g ...

  8. 搞了我一下午竟然是web.config少写了一个点

    Safari手机版居然有个这么愚蠢的bug,浪费了我整个下午,使尽浑身解数,国内国外网站搜索解决方案,每一行代码读了又想想了又读如此不知道多少遍,想破脑袋也想不通到底哪里出了问题,结果竟然是web.c ...

  9. System.DateUtils 3. IsPM、IsAM 判断是否为上、下午

    编译版本:Delphi XE7 function IsPM(const AValue: TDateTime): Boolean; inline;function IsAM(const AValue: ...

随机推荐

  1. [51nod1074]约瑟夫环V2

    N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数.问最后剩下的人的编号. 例如:N = 3,K = 2.2号先出列,然后是1号,最后剩下的是3号. ...

  2. -bash: wget 未找到命令的解决办法

    在Linux操作系统中,我们会经常要用到wget下载文件.wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性. 在linux中使用wget时,若报-bash: wget: comman ...

  3. Maven系列--web.xml 配置详解

    一 .web.xml介绍 启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 紧接着,容 ...

  4. tring.Format格式化用法

    (数字保留两位小数,且每隔3为用逗号隔开): string.format("1f,.2d",333) -->333.00 string.format("1f,.2d ...

  5. Springboot 应用启动分析

    https://blog.csdn.net/hengyunabc/article/details/50120001#comments 一,spring boot quick start 在spring ...

  6. JQuery与CSS之图片上放置button

    position:relative日常应用的时候通常是设置给position:absolute;的父层的, 父层position:relative; 子层position:absolute;的话, 就 ...

  7. U盘无法格式化的恢复

    昨天装Ubuntu的系统可能把U盘搞崩溃了.然后今早起来U盘无法识别,格式化也不行,用Windows的磁盘管理工具格式化说是:Windows无法格式化U盘. 曾经没遇到这样的情况,所以百度了一下,试了 ...

  8. MonkeyRunner源代码分析之启动

    在工作中由于要追求完毕目标的效率,所以很多其它是强调实战.注重招式.关注怎么去用各种框架来实现目的.可是假设一味仅仅是注重招式.缺少对原理这个内功的了解,相信自己非常难对各种框架有更深入的理解. 从几 ...

  9. ubuntu 休眠之后蓝牙鼠标无效果。

    ubuntu链接蓝牙鼠标之后.左上角蓝牙标志左下角应该有一个锁的标志. 可是休眠之后,蓝牙鼠标失效,锁没有了,点击按键,出来锁之后,立即消失. 运行两次例如以下命令能够解决: sudo hciconf ...

  10. nova shelve 的使用

    对于云中的资源我们常有例如以下需求 1,用户对临时不使用的VM进行停止操作.以节省费用. 2.对于长时间未使用的VM.管理员想要从hypervisor层面上清除它们从而节省主机资源. 3.但之前的停止 ...