AYIT-2020 609暑假集训第二周周赛题 题解

A   Supermarket    POJ - 1456 

题意:有n种物品,给出每个物品的价格和保质期,每天只能卖出一个未过期的物品,求商家最大能获利多少钱?

思路:有两种方法可以用贪心写,也可以用并查集写。

贪心代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<stdlib.h>
#include<map>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
#define mod 1000000007
#define eps 1e-9
#define PI acos(-1.0)
#define N 110000
struct node
{
    int x,y;
} q[N];
bool cmp(node a,node b)
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x>b.x;
}
int n,sum,t,book[N];
int main()
{
    while(~scanf("%d",&n))
    {
        mem(book,0);
        for(int i=1; i<=n; i++)
            scanf("%d%d",&q[i].x,&q[i].y);
        sort(q+1,q+1+n,cmp);
        sum=0,book[0]=1;
        for(int i=1; i<=n; i++)
        {
            if(!book[q[i].y])
            {
                sum+=q[i].x;
                book[q[i].y]=1;
            }
            else
            {
                while(q[i].y--)
                {
                    if(!book[q[i].y])
                    {
                        sum+=q[i].x;
                        book[q[i].y]=1;
                        break;
                    }
                }
            }
        }
        printf("%d\n",sum);
    }
}

并查集代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mem(a,b) memset(a,b,sizeof(a))
#define N 10010
int F[N],n;
struct node
{
    int p,d;
} q[N];
bool cmp(node a,node b)//按p从大到小排序。d没有关系
{
    return a.p>b.p;
}
int getf(int x)
{
    if(F[x]==-1)
        return x;
    return F[x]=getf(F[x]);
}
int main()
{
    while(~scanf("%d",&n))
    {
        mem(F,-1);
        for(int i=0; i<n; i++)
            scanf("%d%d",&q[i].p,&q[i].d);
        sort(q,q+n,cmp);
        int ans=0;
        for(int i=0; i<n; i++)
        {
            int t=getf(q[i].d);
            if(t>0)
            {
                ans+=q[i].p;
                F[t]=t-1;
            }
        }
        printf("%d\n",ans);
    }
}

B  Is It A Tree?   POJ - 1308  

题意:给出多组边,让你判断这是不是一棵树。

思路:每输入两个点都判断一下,这两个点连接以后会不会成为环,如果成环就不符合树的定义了,所有数合并之后,遍历一遍所有输入的数,看看他们是不是都有一个共同的祖先,否则那也不是树。(主要是数据处理比较麻烦)

AC代码:

#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
int f[1200000],a[1200000],b[1100000],book[1200000],ww;
int getf(int v)
{
    if(f[v]==v)
        return v;
    else
        return f[v]=getf(f[v]);
}
void merge(int x,int y)
{
    int t1,t2;
    t1=getf(x);
    t2=getf(y);
    if(t1!=t2)
        f[t2]=t1;
    else
        ww++;
}
int main()
{
    int kk=1,aaa,bbb;
    while(~scanf("%d%d",&aaa,&bbb)&&(aaa!=-1||bbb!=-1))
    {
        if(aaa==0&&bbb==0)///进行数据处理
        {
            printf("Case %d ",kk++);
            printf("is a tree.\n");
            continue;
        }
        int maxx=-1,ans=0;
        memset(f,0x3f3f3f3f,sizeof(f));
        memset(book,0,sizeof(book));
        if(book[aaa]==0)
        {
            book[aaa]=1;
            ans++;
        }
        if(book[bbb]==0)
        {
            book[bbb]=1;
            ans++;
        }
        maxx=max(maxx,max(aaa,bbb));
        f[aaa]=aaa;
        f[bbb]=bbb;
        a[0]=aaa;
        b[0]=bbb;
        int t=1,aa,bb;
        while(scanf("%d%d",&aa,&bb)&&(aa||bb))
        {
            maxx=max(maxx,max(aa,bb));
            if(book[aa]==0)
            {
                book[aa]=1;
                ans++;
            }
            if(book[bb]==0)
            {
                book[bb]=1;
                ans++;
            }
            f[aa]=aa;
            f[bb]=bb;
            a[t]=aa;
            b[t]=bb;
            t=t+1;
        }
        ww=0;
        int sum=0;
        for(int i=0; i<t; i++)
            merge(a[i],b[i]);
        for(int i=0; i<=maxx; i++)
        {
            if(f[i]==0x3f3f3f3f)
                continue;
            if(f[i]==i)
                sum++;
        }
        printf("Case %d ",kk++);
        if(sum==1&&ans-1==t&&ww==0)
            printf("is a tree.\n");
        else
            printf("is not a tree.\n");
    }
}

C  The Accomodation of Students  HDU - 2444 

题意:有n个学生,有m对人是认识的,每一对认识的人可以分到一个房间,问能否把n个学生分成两部分,每部分内的学生互不认识,而两部分之间的学生认识。如果可以分成两部分,就算出房间最多需要多少间,否则就输出No。

思路:判断是否为二分图,然后判断最大匹配。  

AC代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define eps 1e-8
#define mod 1000000007
#define lowbit(x) x&(-x)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define PI acos(-1.0)
#define inf 0x3f3f3f3f
#define N 210
int n,m,book[N],vis[N];
int a,b,s[N][N];
int bfs(int x)
{
    queue<int>q;
    q.push(1);
    book[1]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=1; i<=n; i++)
        {
            if(book[i]==0&&s[u][i])
            {
                book[i]=-book[u];
                q.push(i);
            }
            else if(book[i]==book[u]&&s[u][i])
                return 0;
        }
    }
    return 1;
}
int find1(int x)
{
    for(int i=1; i<=n; i++)
        if(vis[i]==0&&s[x][i])
        {
            vis[i]=1;
            if(book[i]==-1||find1(book[i]))
            {
                book[i]=x;
                return 1;
            }
        }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        mem(s,0),mem(book,0);
        for(int i=1; i<=m; i++)
            scanf("%d%d",&a,&b),s[a][b]=1;
        int flag=bfs(1);
        if(!flag)
            printf("No\n");
        else
        {
            int sum=0;
            mem(book,-1);
            for(int i=1; i<=n; i++)
            {
                mem(vis,0);
                if(find1(i))
                    sum++;
            }
            printf("%d\n",sum);
        }
    }
}

D   畅通工程再续   HDU - 1875 

题意:中文题,不在解释了

思路:只有当前岛屿和其他的距离在10到1000这个范围内就可以相连到一块,我们可以把这些相连的岛屿存放到结构体中,然后跑一遍最小生成树就可以了。

AC代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<math.h>
#include<vector>
#include<map>
#include<iostream>
using namespace std;

#define PI acos(-1.0)
#define ll long long
#define eps 1e-8
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 1000000007
#define inf 0x3f3f3f3f
#define N 110

struct node
{
    int x,y;
    double dis;
} q[50000];

int t;
int n,m;
int f[N];
int a[N],b[N];

bool zmh(node a,node b){return a.dis<b.dis;}

int getf(int v)
{
    if(f[v]==v) return v;
    else return f[v]=getf(f[v]);
}
void kruskal(int cnt)
{
    int num=0;
    double ans=0.0;
    for(int i=0; i<cnt; i++)
    {
        int t1=getf(q[i].x);
        int t2=getf(q[i].y);
        if(t1!=t2&&q[i].dis>=10&&q[i].dis<=1000){
            f[t1]=t2;
            num++;
            ans+=q[i].dis;
        }
        if(num==n-1||q[i].dis>1000)break;
    }
    if(num==n-1)printf("%.1f\n",ans*100);
    else printf("oh!\n");
    return ;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        mem(f,0);
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            f[i]=i;
        }
        int k=0;
        for(int i=1; i<=n; i++)
            for(int j=i+1; j<=n; j++)
            {
                q[k].x=i;
                q[k].y=j;
                q[k++].dis=sqrt(1.0*(a[j]-a[i])*(a[j]-a[i])+1.0*(b[j]-b[i])*(b[j]-b[i]))*1.0;
            }
        sort(q,q+k,zmh);
        kruskal(k);
    }
}

E  Invitation Cards  POJ - 1511 

题意:有n个车站,m条公交车路线,每条公交车路线都有一个价格。要求从起始站到每一站,然后再从每一站回到起始站,求最少花的钱数。(注意是m条单向边)

思路:用spfa算法正向建边求出一个dis1 [ i ] 的数组,表示起始站到 i 站的最少花费,再用spfa算法反向建边求出一个dis2 [ i ] 的数组,表示 i 站到起始站的最少花费,然后把两个数组对应相加求和就是最终结果。

AC代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
int first[1100000],next[1100000],u[1100000],v[1010000],w[1100000];
int book[1100000],dis1[1100000],dis2[1100000];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int tt=0;
        memset(first,-1,sizeof(first));
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            next[i]=first[u[i]];///正向建边
            first[u[i]]=i;
        }
        memset(book,0,sizeof(book));
        memset(dis1,0x3f3f3f3f,sizeof(dis1));
        memset(dis2,0x3f3f3f3f,sizeof(dis2));
        queue<int>q;
        q.push(1);
        dis1[1]=0;
        while(!q.empty())///spfa算法
        {
            int x=q.front();
            q.pop();
            book[x]=0;
            for(int j=first[x]; j!=-1; j=next[j])
            {
                if(dis1[v[j]]>dis1[x]+w[j])
                {
                    dis1[v[j]]=dis1[x]+w[j];
                    if(book[v[j]]==0)
                    {
                        book[v[j]]=1;
                        q.push(v[j]);
                    }
                }

            }
        }

        memset(book,0,sizeof(book));
        memset(first,-1,sizeof(first));
        for(int i=1; i<=m; i++)///反向建边
        {
            next[i]=first[v[i]];
            first[v[i]]=i;
        }
        queue<int>Q;
        Q.push(1);
        dis2[1]=0;
        while(!Q.empty())///spfa算法
        {
            int x=Q.front();
            Q.pop();
            book[x]=0;
            for(int j=first[x]; j!=-1; j=next[j])
            {
                if(dis2[u[j]]>dis2[x]+w[j])
                {
                    dis2[u[j]]=dis2[x]+w[j];
                    if(book[u[j]]==0)
                    {
                        book[u[j]]=1;
                        Q.push(u[j]);
                    }
                }

            }
        }
        long long sum=0;
        for(int i=1; i<=n; i++)///对应相加求和
            sum=sum+dis1[i]+dis2[i];
        printf("%lld\n",sum);
    }
    return 0;
}

F  Invade the Mars  HDU - 3873 

题意:就是美国要从 1 攻打火星中心N(就是求从1到N的最短距离),但是会给一些 A 城市被 B 城市保护,要想攻打A 城市就必须先攻打 B 城市。

思路:(最短路径+Dijkstra(稍微有点修改))用num[i]标记第i个城市被保护的数目,只有当该点被保护的数目为0时,才能入S集合,从而优化到其他点的时间。当前进入S集合的城市,遍历它所保护的城市,num[i]减一,记录下被保护的城市解除保护所需的最长时间。说的有点绕,看代码会比较清楚。

AC代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
#define N 70005
#include<algorithm>
using namespace std;
int book[3005];//标记这个城市是否遍历过
int root[3005];//例root[i]表示起点到i城市的最远距离,i是被保护的城市。
int num[3005];//记录城市i被多少个城市保护
int e[3005][3005];//两城市之间的最短距离
int dis[3005];//起点到i之间的最短距离
vector<int>v[3005];//例如v[u][i],表示u城市保护i城市
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,a,b,c,h;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)//容器清空
            v[i].clear();
        memset(root,0,sizeof(root));
        memset(book,0,sizeof(book));
        memset(e,0x3f3f3f3f,sizeof(e));
        memset(dis,0x3f3f3f3f,sizeof(dis));
        memset(num,0,sizeof(num));
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            e[a][b]=min(e[a][b],c);
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&num[i]);
            for(int j=1; j<=num[i]; j++)
            {
                scanf("%d",&h);
                v[h].push_back(i);
            }
        }
        dis[1]=0;
        for(int i=1; i<=n; i++)
        {
            int minn=0x3f3f3f3f,u;//u是保护的城市
            for(int j=1; j<=n; j++)
            {
                dis[j]=max(dis[j],root[j]);//每遍历一个点都要更新一下
                if(dis[j]<minn&&book[j]==0&&num[j]==0)
                {
                    u=j;
                    minn=dis[j];
                }
            }
            if(minn==0x3f3f3f3f)
                break;
            book[u]=1;
            for(int j=0; j<v[u].size(); j++)
            {
                int k=v[u][j];//k是被保护的城市
                num[k]--;
                root[k]=max(dis[u],root[k]);//选择最大的时间
            }
            v[u].clear();//用过之后要清空
            for(int j=1; j<=n; j++)
                if(dis[j]>dis[u]+e[u][j])
                    dis[j]=dis[u]+e[u][j];
        }
        printf("%d\n",dis[n]);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值