poj1011Sticks
Description
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个单位长度。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
Input
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
Output
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
Sample Input
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
Sample Output
6
5
初始长度一定不小于当前最长木棍的长度,为总长度的约数,枚举当前最长木棍的长度到总长度,看能否把所有木棍分别拼成这个长度,注意木棍是否被使用,当前拼好的长度不能超出范围,看能否把所有木棍分别拼成这个长度,不过直接搜索会超时,需要剪枝,如下:
1.在拼凑一根木棒时,前面的木棍已枚举过,从上一次的位置开始枚举即可
2.前面某个长度的木棍没有被使用,说明这个长度的木棍当前尝试失败,直接跳过长度相等的木棍,找长度不相等的木棍
3.尝试失败后,如果当前拼成的长度为0,说明当前怎样都无法拼成,返回上一步,如果加上当前木棍刚好拼成一个木棒,说明剩下的木棍无法拼成,返回上一步
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=74;
int n,len,a[N];
bool vis[N];
bool dfs(int cnt,int sum,int pos)//已使用的木棍数量,当前拼好的长度,当前枚举的位置
{
if(sum==len)
{
if(cnt==n)return true;
else return dfs(cnt,0,0);
}
for(int i=pos;i<n;++i)
{
if(i!=0&&a[i]==a[i-1]&&!vis[i-1])continue;
if(!vis[i]&&sum+a[i]<=len)
{
vis[i]=true;
if(dfs(cnt+1,sum+a[i],i+1))
return true;
vis[i]=false;
if(sum==0||sum+a[i]==len)return false;
}
}
return false;
}
int main()
{
while(scanf("%d",&n),n)
{
int sum=0;
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a,a+n,greater<int>());
for(int i=a[0];i<=sum;++i)
{
if(sum%i==0)
{
len=i;
memset(vis,0,sizeof vis);
if(dfs(0,0,0))
{
printf("%d\n",len);
break;
}
}
}
}
return 0;
}
acwing1126最小花费
在 n 个人中,某些人的银行账号之间可以互相转账。
这些人之间转账的手续费各不相同。
给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A 最少需要多少钱使得转账后 B 收到 100 元。
输入格式
第一行输入两个正整数 n,m,分别表示总人数和可以互相转账的人的对数。
以下 m 行每行输入三个正整数 x,y,z,表示标号为 x 的人和标号为 y 的人之间互相转账需要扣除 z% 的手续费 ( z<100 )。
最后一行输入两个正整数 A,B。
数据保证 A 与 B 之间可以直接或间接地转账。
输出格式
输出 A 使得 B 到账 100 元最少需要的总费用。
精确到小数点后 8 位。
数据范围
1
≤
n
≤
2000
1≤n≤2000
1≤n≤2000,
m
≤
1
0
5
m≤10^5
m≤105
输入样例:
3 3
1 2 1
2 3 2
1 3 3
1 3
输出样例:
103.07153164
x给y转账有a ∗ * ∗(1-c%)=b,即a ∗ * ∗p=b,同理A给B转账有 a ∗ p 1 ∗ p 2 ∗ … ∗ p i a*p_1*p_2*…*p_i a∗p1∗p2∗…∗pi=b,当 p 1 ∗ p 2 ∗ … ∗ p i p_1*p_2*…*p_i p1∗p2∗…∗pi最大时,到账100元所需的费用最小
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2010;
int n,m;
double map[N][N],dis[N];
bool vis[N];
double dijsktra(int st,int ed)
{
dis[st]=1;
for(int i=0;i<n;++i)
{
int k=1;
double MAX=0;
for(int j=1;j<=n;++j)
if(dis[j]>MAX&&!vis[j])
{
k=j;
MAX=dis[j];
}
vis[k]=true;
for(int j=1;j<=n;++j)
dis[j]=max(dis[j],dis[k]*map[k][j]);
}
return dis[ed];
}
int main()
{
int x,y,z;
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
map[x][y]=map[y][x]=max((100-z)/100.0,map[x][y]);
}
scanf("%d%d",&x,&y);
printf("%.8lf",100/dijsktra(x,y));
return 0;
}
acwing1127香甜的黄油
农夫John发现了做出全威斯康辛州最甜的黄油的方法:糖。
把糖放在一片牧场上,他知道 N 只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。
当然,他将付出额外的费用在奶牛上。
农夫John很狡猾,就像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。
他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。
农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。
给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。
数据保证至少存在一个牧场和所有牛所在的牧场连通。
输入格式
第一行: 三个数:奶牛数 N,牧场数 P,牧场间道路数 C。
第二行到第 N+1 行: 1 到 N 头奶牛所在的牧场号。
第 N+2 行到第 N+C+1 行:每行有三个数:相连的牧场A、B,两牧场间距D,当然,连接是双向的。
输出格式
共一行,输出奶牛必须行走的最小的距离和。
数据范围
1≤N≤500,
2≤P≤800,
1≤C≤1450,
1≤D≤255
输入样例:
3 4 5
2
3
4
1 2 1
1 3 5
2 3 7
2 4 3
3 4 5
输出样例:
8
开始是保存了每个牧场中牛的数量,在最短路更新时用牧场间距乘牛的数量作为距离下一个点的权值,但如果这么做,在更新一个没有牛的牧场时中间权值会变为0,实际上牛经过牧场是有花费的,因此不能这么做
“数据保证至少存在一个牧场和所有牛所在的牧场连通"指所有有牛的牧场都是连通的,没有牛的牧场就不一定,当一个点无法到达其他点,即距离为无穷时处理一下即可
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=810,M=510,INF=0x3f3f3f3f;
typedef pair<int,int>PII;
int m,n,q,dis[N],a[M];
bool vis[N];
vector<PII>g[N];
int dijsktra(int x)
{
int sum=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push({0,x});
dis[x]=0;
while(heap.size())
{
PII t=heap.top();
heap.pop();
if(vis[t.second])continue;
int now=t.second;
vis[now]=true;
for(int i=0;i<g[now].size();++i)
{
int next=g[now][i].first;
int d=g[now][i].second;
if(dis[next]>dis[now]+d)
{
dis[next]=dis[now]+d;
heap.push({dis[next],next});
}
}
}
for(int i=0;i<m;++i)
{
if(dis[a[i]]==INF)return INF;
sum+=dis[a[i]];
}
return sum;
}
int main()
{
int x,y,z,sum=INF;
scanf("%d%d%d",&m,&n,&q);
for(int i=0;i<m;++i)scanf("%d",&a[i]);
while(q--)
{
scanf("%d%d%d",&x,&y,&z);
g[x].push_back({y,z});
g[y].push_back({x,z});
}
for(int i=1;i<=n;++i)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
sum=min(sum,dijsktra(i));
}
printf("%d\n",sum);
return 0;
}
acwing1128信使
战争时期,前线有 n 个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。
信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。
指挥部设在第一个哨所。
当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。
当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。信在一个哨所内停留的时间可以忽略不计。
直至所有 n 个哨所全部接到命令后,送信才算成功。
因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他 k 个哨所有通信联系的话,这个哨所内至少会配备 k 个信使)。
现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。
输入格式
第 1 行有两个整数 n 和 m,中间用 1 个空格隔开,分别表示有 n 个哨所和 m 条通信线路。
第 2 至 m+1 行:每行三个整数 i、j、k,中间用 1 个空格隔开,表示第 i 个和第 j 个哨所之间存在 双向 通信线路,且这条线路要花费 k 天。
输出格式
一个整数,表示完成整个送信过程的最短时间。
如果不是所有的哨所都能收到信,就输出-1。
数据范围
1≤n≤100,
1≤m≤200,
1≤k≤1000
输入样例:
4 4
1 2 4
2 3 7
2 4 1
3 4 6
输出样例:
11
为什么不能用最小生成树求最小边权和?因为每个哨所向相连哨所送信,相连哨所就要更新第一个哨所到自己的位置所花费的时间,为了取得总更新中的最大值,所以当所有哨所都能达到时,花费最多的时间即为答案
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=110;
int n,m,map[N][N],dis[N];
bool vis[N];
int dijsktra()
{
int ans=0;
for(int i=1;i<=n;++i)
dis[i]=map[1][i];
dis[1]=0;
vis[1]=true;
for(int i=1;i<n;++i)
{
int MIN=0x3f3f3f3f,k=1;
for(int j=1;j<=n;++j)
if(MIN>dis[j]&&!vis[j])
{
MIN=dis[j];
k=j;
}
if(vis[k])return -1;
vis[k]=true;
for(int j=1;j<=n;++j)
dis[j]=min(dis[j],dis[k]+map[k][j]);
}
for(int i=1;i<=n;++i)
ans=max(ans,dis[i]);
return ans;
}
int main()
{
int x,y,z;
memset(map,0x3f,sizeof map);
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
if(z<map[x][y])
map[x][y]=map[y][x]=z;
}
printf("%d\n",dijsktra());
return 0;
}