oj练习记录

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 1n2000,
m ≤ 1 0 5 m≤10^5 m105

输入样例:

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 ap1p2pi=b,当 p 1 ∗ p 2 ∗ … ∗ p i p_1*p_2*…*p_i p1p2pi最大时,到账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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值