0 最小比例(ratio)
图中共有N个点的完全图,每条边都有权值,每个点也有权值。要求选出M个点和M-1条边,构成一棵树,使得:
即所有边的权值与所有点的权值之和的比率最小。
给定N和M,以及N个点的权值,和所有的边权,要求M个点的最小比率生成树。
N和M(2<=N<=15,2<=M<=N)
15的数据,暗示着这题不寻常的写法——暴搜
递归求所有点的排列(显然15是不会爆的
这样的就把点的权值固定了
然后每次求出的排列求最小生成树,求出最小的边权和
再用求出的点权和与边权和的比值更新答案,记录最小的生成树
因为是按照顺序递归的,当比率相同时不用更新答案就可以保证生成树字典序最小
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,ss,nod,edg=10000;
int d[20],cnt,f[20];
struct cv{
int ed,x,y;
}e[400];
bool comp(cv a,cv b){
return a.ed<b.ed;
}
int get(int x){
if (f[x]==x) return x;
f[x]=get(f[x]);
return f[x];
}
void tree(int x){
int node=0,edge=0,b[20];
for (int i=1;i<=n;i++){
f[i]=i;
if ((x&(1<<(i-1)))>0) node+=d[i];
}
for (int i=1,j=0;i<=cnt&&j<m;i++)
if ((x&(1<<(e[i].x-1)))&&(x&(1<<(e[i].y-1)))&&get(e[i].x)!=get(e[i].y)){
edge+=e[i].ed;
f[f[e[i].x]]=f[e[i].y];
j++;
}
if (edge*nod<edg*node){
edg=edge,nod=node,ss=x;
return;
}
}
void dfs(int dep,int k,int s){
if (k>=m){
tree(s);
return;
}
if (dep>=n) return;
dfs(dep+1,k+1,s|1<<dep);
dfs(dep+1,k,s);
}
int main(){
freopen("ratio.in","r",stdin);
freopen("ratio.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&d[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
int cd;
scanf("%d",&cd);
if (i!=j) e[++cnt].x=i,e[cnt].y=j,e[cnt].ed=cd;
}
sort(e+1,e+1+cnt,comp);
dfs(0,0,0);
for (int i=1;i<=n;i++)
if ((ss&1<<(i-1))>0)
printf("%d ",i);
}
1 软件公司(company)
一家软件开发公司有两个项目,并且这两个项目都由相同数量的m个子项目组成,对于同一个项目,每个子项目都是相互独立且工作量相当的,并且一个项目必须在m个子项目全部完成后才算整个项目完成。
这家公司有n名程序员分配给这两个项目,每个子项目必须由一名程序员一次完成,多名程序员可以同时做同一个项目中的不同子项目。
求最小的时间T使得公司能在T时间内完成两个项目。
m(1<=n<=100,1<=m<=100)。
其实仔(sui)细(bian)思(xiang)考(xiang)可以得出,题目就是要求一个所有程序员中最大的完成时间最少的方案,求这个最小的最大完成时间
于是就选择二分答案,dp判断答案正确性
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第i个程序员,第一个任务完成了j个,还可以完成最多个数的第二个任务
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
k
]
+
(
m
i
d
−
a
[
i
]
∗
k
)
/
b
[
i
]
)
f[i][j]=max(f[i-1][j-k]+(mid-a[i]*k)/b[i])
f[i][j]=max(f[i−1][j−k]+(mid−a[i]∗k)/b[i])注意不要让f出现负数的情况即转移时要保证
k
∗
a
[
i
]
<
=
m
i
d
k*a[i]<=mid
k∗a[i]<=mid
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,l,r;
int a[105],b[105];
int f[105][105];
void read(){
scanf("%d%d",&n,&m);
int xa=0,xb=0;
for (int i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
xa=(xa<a[i]?a[i]:xa),
xb=(xb<b[i]?b[i]:xb),
}
l=1,r=xa*m+xb*m;
}
bool dp(int x){
memset(f,-10,sizeof f);
for (int i=0;i<=n;i++)
f[i][0]=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
for (int k=0;k<=j&&k*a[i]<=x;k++)
f[i][j]=max(f[i-1][j-k]+(x-a[i]*k)/b[i],f[i][j]);
if (f[n][m]>=m) return 1;
return 0;
}
void bs(){
int t;
while (l<=r){
int mid=(l+r)/2;
if (dp(mid)) t=mid,r=mid-1;else l=mid+1;
}
printf("%d",t);
}
int main(){
freopen("company.in","r",stdin);
freopen("company.out","w",stdout);
read();
bs();
}
2 空间航行(warp)
你是一艘战列巡洋舰的引擎操作人员,这艘船的船员在空间中侦测到了一些无法辨识的异常信号。你的指挥官给你下达了命令,让你制定航线,驾驶战列巡洋舰到达那里。
船上老旧的曲速引擎的速度是0.1AU/s。然而,在太空中分布着许多殖民星域,这些星域可以被看成一个球。在星域的内部,你可以在任何地方任意次跳跃到星域内部的任意一个点,不花费任何时间。
你希望算出到达终点的最短时间。
每个输入文件至多包含10 个测试数据。
对于10% 的数据,n = 0。
对于30% 的数据,0<=n<=10。
对于100% 的数据,0<=n<=100,所有坐标的绝对值<=10000 ,半径r<=10000。
你可以认为,你所在的星区的大小为无限大。
虽然是三维的地图,但是可以转化成有n个点的无向图
星球与星球的距离是球心距离减去半径,起点终点也算做半径为0的星球
然后随便最短路,弗洛伊德spfaDij随便跑
我一开始老错,最后发现是Floyed打炸??
原来Floyed原理是枚举中间点更新枚举出来的路径,也就是说中间点k应放在第一重循环
#include <cstdio>
#include <math.h>
#include <cstring>
using namespace std;
int n,m;
int x[105],y[105],z[105],r[105];
double a[105][105];
double kk(int x,int y,int z,int a,int b,int c,int r,int d){
double cd=sqrt((x-a)*(x-a)+(y-b)*(y-b)+(z-c)*(z-c));
cd=(double)cd-r-d;
if (cd<0) return 0;
return cd;
}
int main(){
freopen("warp.in","r",stdin);
freopen("warp.out","w",stdout);
while (1){
scanf("%d",&n);
if (n==-1) break;
for (int i=1;i<=n;i++){
scanf("%d%d%d%d",&x[i],&y[i],&z[i],&r[i]);
}
scanf("%d%d%d",&x[0],&y[0],&z[0]);
r[n+1]=0;
scanf("%d%d%d",&x[n+1],&y[n+1],&z[n+1]);
for (int i=0;i<=n+1;i++)
for (int j=0;j<=n+1;j++)
a[i][j]=a[j][i]=kk(x[i],y[i],z[i],x[j],y[j],z[j],r[i],r[j]);
for (int i=0;i<=n+1;i++)
for (int j=0;j<=n+1;j++)
if (i!=j)
for (int k=0;k<=n+1;k++)
if (j!=k&&i!=k&&a[i][k]+a[k][j]<a[i][j])
a[i][j]=a[j][i]=a[i][k]+a[k][j];
int ans=floor(a[0][n+1]*10+0.5);
printf("%d\n",ans);
}
}
3 摧 毁 巴士站(bus)
Gabiluso是最伟大的间 谍之一。现在,他试图完成一个“不可能完成”的使命――减缓Colugu的军 队到达机场的时间。Colugu有n个公共汽车站和m条道路。每条道路直接连接两个巴士站,所有的道路都是单向的。为了保持空气洁净,政 府禁 止所有军 用 车辆,因此,军队必须乘搭巴士去机场。两个巴士站之间,可能有超过一条道路。如果一个公共汽车站被破坏时,所有连接该站的道路将无法运作。Gabiluso需要做的是 摧 毁 了一些公共汽车站,使军 队无 法在K分钟内到达机场。一辆公交车通过一条道路,都是一分钟。所有巴士站的编号从1到n。1号巴士站是在军营,第n号站是机场。军队始终从第一站出发。第一站和第n站不能被破坏,这里有大量的防御力量。当然也没有从第一站到第n站的道路。
请帮助Gabiluso来计算能完成使命所需摧 毁的最低数目的巴士站。
第一行包含三个整数n,m,k (2<n<=50,0<m<=4000,0<k<1000)。
接下来m行,每行2个整数s和f,表示从站s到站f有一条路。
50的数据暗示着这道题不同寻常的写法——我当时就想,要么暴搜,要么状压DP
然后它就真的暴搜了。。。
每次都跑最短路它真的没有炸。。。
就每次都跑最短路,然后枚举删除最短路经过的点,递归枚举,如果最短路长度>k就更新答案并退出,算是找到一个方案
然后它就过了???
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,k,ans=55;
int a[55][55],s[55];
int v[40005],bz[55],d[55];
void read(){
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
a[u][v]=1;
}
}
void spfa(){
int h=0,t=1,b[55];
memset(b,0,sizeof b);
memset(d,0x3f,sizeof d);
v[1]=1,b[1]=1,d[1]=0;
while (h<t){
int u=v[++h];
for (int i=1;i<=n;i++){
if (a[u][i]>0&&bz[i]==0&&d[v[h]]+a[u][i]<d[i]){
d[i]=d[v[h]]+a[u][i];
s[i]=v[h];
if (b[i]==0){
b[i]=1;
v[++t]=i;
}
}
}
b[v[h]]=0;
}
}
void dfs(int x){
if (x>ans) return;
spfa();
if (d[n]>k){
ans=min(ans,x);
return;
}
int q[55];
memcpy(q,s,sizeof s);
for (int i=q[n];i>1;i=q[i]){
bz[i]=1;
dfs(x+1);
bz[i]=0;
}
}
int main(){
freopen("bus.in","r",stdin);
freopen("bus.out","w",stdout);
read();
dfs(0);
printf("%d",ans);
}