XMUT 简单图论专场(拓扑排序、最小生成树、贪心)

来源:互联网 发布:淘宝寄澳洲,电话号码 编辑:程序博客网 时间:2024/06/03 05:04


A - 小Y上学记——修学分


Problem Description

小Y终于如愿以偿地通过高考来到了魂牵梦萦的大学校园——ACdream大学。来到校园的第一件事就是选课。

由于每一门课都有1个学分~而且有一些课需要先学完别的课程(例如必须先学会高等数学,才能学会量子力学,必须先学会走,才能学会跑)

ACdream大学需要学生修够若干学分才允许毕业。

请按顺序输出小Y的一种方案(若不止一种答案,请输出字典序最小的一种方案)

Input

多组数据,每组数据首先是两个整数n,m,k,分别表示学科总数,学科之间的关系数,以及毕业所需的最少学分。

(2<=n<=100, 0<=m<=500,1<=k<=n)

接下来是m行,每行是两个整数a,b表示学科a是学科b的前置学科。

Output

对于每组数据,若小Y不存在任何方案选课,请输出-1.

否则在一行输出m个整数,表示小Y按顺序修的学科编号。

Sample Input

2 1 20 12 2 21 00 13 2 11 00 13 0 3

Sample Output

0 1-120 1 2

Hint

对于第一组数据,先修完第0学科,获得1学分,再修以第0学科为前置学科的第1学科,获得1学分,即可满足毕业条件:2学分。

对于第二组数据,由于第0学科的前置学科为第1学科,而第1学科的前置学科为第2学科,因此小Y无论如何也无法满足毕业条件。

对于第三组数据,由于多了第2学科,而且第2学科不需要前置学科,因此只需要修完第2学科即可满足毕业条件了。

对于第四组数据,没有任何的先后限制,因此6种全排列方案均符合题意,字典序最小的方案为0,1,2


这里有一道我做过的类似题目可供参考:http://blog.csdn.net/enjoying_science/article/details/47380099


编程思想:根据题意,可抽象成图的拓扑排序。这里采用map和链式前向星两种方法来实现拓扑排序算法。


AC  code1(map方式):

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<string>#include<algorithm>#include<iostream>#define LL long long #define exp 1e-10#define MAXN 1000010using namespace std;const int INF=0x3f3f3f3f;const int N = 100005;const int mod = 1000000007;int ma[555][555];int ind[555];int ans[555];int main(){//freopen("D:\in.txt","r",stdin);int n,m,k,i,j,cnt;while(scanf("%d%d%d",&n,&m,&k)!=EOF){memset(ma,0,sizeof(ma));memset(ind,0,sizeof(ind));for(i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);if(!ma[a][b]){ma[a][b]=1;ind[b]++;}}int node;cnt=0;for(i=0;i<n;i++)//i为成功拓扑排序的点的个数 {for(j=0;j<n;j++)//点的编号是从0开始的 {if(ind[j]==0){node=j;ind[node]--;ans[i]=node;for(int jj=0;jj<n;jj++){if(ma[node][jj]){ind[jj]--;}}break;}}if(j==n)//点的编号是从0开始的,到n-1都遍历完,直接跳出break;}if(i<k)//i为成功拓扑排序的点的个数 {printf("-1\n");}else{printf("%d",ans[0]);for(i=1;i<k;i++){printf(" %d",ans[i]);}printf("\n");}}return 0;}


AC code2(链式前向星):

#include <iostream>#include <stdio.h>#include <string.h>#include <map>#define MAX 505using namespace std;int InD[505];/*InD[i]记录点i的入度*/int First[MAX];/*First[i]头结点的第一条边的编号*/struct edge{    int TO;/*点*/    int Next;/*下一条边的编号*/}ID[3*MAX];int SIGN;void Add_E(int x,int y)/*添加点操作*/{    ID[SIGN].TO=y;    InD[y]++;    ID[SIGN].Next=First[x];    First[x]=SIGN++;}int Jude(int x,int y)/*查找与X是否与Y相连*/{    int i;    for(i=First[x];i!=0;i=ID[i].Next)   //查找与该点相关的点    {       if(ID[i].TO==y)return 0;    }    return 1;}int ToPoSort(int N,int Num[],int K)/*拓扑排序,邻接表*/{    int i,j,k;    for(j=0;j<N;j++)    {        for(i=1;i<=N;i++)        {            if(InD[i]==0)            {                InD[i]--;                Num[j]=i;                for(k=First[i];k!=0;k=ID[k].Next)                {                    InD[ID[k].TO]--;                }                break;            }        }        if(i>N)break;    }    if(j>=K)return 1;    else return 0;}int main(){    int M,N,K,i;    int a,b;    int Num[MAX];    while(scanf("%d%d%d",&N,&M,&K)!=EOF)    {        for(i=1;i<=N;i++){First[i]=0;InD[i]=0;}        for(i=0,SIGN=1;i<M;i++)        {            scanf("%d%d",&a,&b);a+=1;b+=1;            Add_E(a,b);        }        if(ToPoSort(N,Num,K))/*拓扑排序*/        {            for(i=0;i<K;i++)            {                if(i!=0)putchar(32);                printf("%d",Num[i]-1);            }putchar(10);        }        else printf("-1\n");    }    return 0;}


B - 小Y上学记——小Y的玩偶

Problem Description

小Y最喜欢拆拆拆了~尽管他不一定能装回去。

小Y有一个很可爱的积木玩偶,是由一块一块积木拼接而成,现在小Y想把这个积木玩偶拆拆拆。

每一块积木玩偶都有一个耐久值,想把一块积木拆出来,小Y需要付出的能量就是和它直接拼接的所有积木的耐久值之和。

小Y很懒的~他想知道把这个玩偶全部拆好,最少需要付出多少能量?

Input

多组数据,每组数据首先是一个整数n表示积木块数。(0<n<=1000)

接下来一行包含n个整数,表示每块积木的耐久值a[i](0<=a[i]<=100000)。

接下来是n行,第i行代表第i块积木的连接情况。

每一行首先是一个整数k,表示这块积木与k块积木相连,接下来是k个整数,代表与这块积木相连的积木标号(标号从0开始)

保证连接情况合法。

Output

对于每组数据,输出一个整数,表示小Y需要的最少能量。

Sample Input

410 20 30 402 1 32 0 21 11 04100 100 100 1001 13 0 2 32 1 32 1 2740 10 20 10 20 80 404 2 3 4 51 42 0 35 0 2 4 5 64 0 1 3 62 0 32 3 4

Sample Output

40400160

Hint

对于第一组数据,首先拆掉第2块积木,需要20能量,然后拆掉第1块积木,需要10能量,接着拆掉第3块积木,需要10能量,最后只剩下第0块了,不需要能量了。总共需要40点能量。

对于第二组数据,无论怎么拆除,都是需要400点能量。


编程思想:贪心+邻接表。每次都拆耐久值最大的点,然后更新与其连接的点的信息,直到图中剩下一个点,这时所得的能量和最小。下面证明这种贪心策略是正确的。每次选择耐久度最大的点,设这个点的耐久度为K,有m个点与其连接,这m个点的耐久度为K1~Km,若不取走这个耐久度最大的点,那么要拆了连接它的m条边的点,则需要消耗K*m能量,如果直接把这个点拆除了,则需要K1+K2+...+Km能量。因为K*m>=K1+K2+...+Km,所以拆掉连接的这相同的m条边时,后者消耗的能量更少。因此,每次拆除耐久度最大的点,然后再去更新连接图,这样可以保证消耗的能量最小。


AC code:

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<string>#include<algorithm>#include<iostream>#define LL long long #define exp 1e-10#define MAXN 1000010using namespace std;const int INF=0x3f3f3f3f;const int N = 100005;const int mod = 1000000007;int n,k,m,mi;int p[MAXN]; struct node{int v;int id;}a[MAXN];int b[MAXN];bool cmp(node a,node b){return a.v>b.v;}vector<int>vec[MAXN];bool vis[MAXN];LL ans,mm;int main(){//freopen("D:\in.txt","r",stdin);int i,j,t;while(scanf("%d",&n)!=EOF){for(i=0;i<n;i++){scanf("%d",&a[i].v);a[i].id=i;}sort(a,a+n,cmp);for(i=0;i<n;i++)b[a[i].id]=i;memset(p,0,sizeof(p));memset(vis,false,sizeof(vis));for(i=0;i<n;i++)vec[i].clear();mm=-INF;mi=0;for(i=0;i<n;i++){scanf("%d",&k);while(k--){scanf("%d",&t);vec[i].push_back(t);p[i]+=a[b[t]].v;}}ans=0;for(j=0;j<n-1;j++){mi=a[j].id;ans+=p[mi];for(i=0;i<vec[mi].size();i++)//更新连接的点{p[vec[mi][i]]-=a[b[mi]].v;}a[b[mi]].v=0;//0表示已删除,不再对与它连接的点有关系,即“惰性删除”}printf("%lld\n",ans);}return 0;}





C - 小Y上学记——认识新同学

Problem Description

小Y来到了一个新的班级,新的集体,第一件事肯定是要去认识新同学。

然而想认识全班同学,所需要的代价是很大的,所以小Y想让代价尽可能小。

认识新同学有两种办法,一种是直接去认识,另一种是通过已经认识的同学去认识新同学。

小Y想知道最小代价是多少呢?

Input

多组数据,每组数据首先是一个N,表示新的班级的同学数(包括小Y,2<=N<=1000)

接下来是N-1个整数,表示小Y直接认识第i名同学所需要的代价。

接下来是一个N-1阶矩阵,其中第i行第j列表示通过第i名同学认识第j名同学的代价。

其中保证对角线为0,且保证对称.

所有代价均为不超过1000的正整数。

Output

对于每组数据,输出一个整数,表示最少代价。

Sample Input

32 40 11 042 4 30 1 21 0 12 1 0

Sample Output

34

编程思想:根据题意,可抽象成求一颗最小生成树。这里采用利用并查集构造最小生成树的克鲁斯卡尔算法实现。


AC  code:

#include<iostream>#include<algorithm>#include<cmath>#include<cstring>#include<cstdio>#include<queue>#include<vector>#define LL long long#define MAXN 1000010using namespace std;const int INF=0x3f3f3f3f;struct node{    int u;    int v;    int w;}e[MAXN];int point[MAXN],f[MAXN];int ans,n,p,cnt;bool cmp(node a,node b){    return a.w<b.w;}void init(int n){    for(int i=1;i<=n;i++)        f[i]=i;}int fin(int root){    int son,tem;    son=root;    while(root!=f[root])    {        root=f[root];    }    while(son!=root)    {        tem=f[son];        f[son]=root;        son=tem;    }    return root;}bool join(int r1,int r2){    int x=fin(r1);    int y=fin(r2);    if(x!=y)    {        f[x]=y;        return true;    }    return false;}int clu(){    ans=0;    for(int i=1;i<=p;i++)    {        int u=e[i].u;        int v=e[i].v;        if(join(u,v))        {            ans+=e[i].w;            cnt++;                       }    }    return ans;}int main(){    //freopen("D:\in.txt","r",stdin);    int i,j,l,tt;    while(scanf("%d",&n)!=EOF)    {        p=0;        for(i=2;i<=n;i++)        {            p++;            e[p].u=1;            e[p].v=i;            scanf("%d",&e[p].w);        }        for(i=2;i<=n;i++)        {            for(j=2;j<=n;j++)            {                   scanf("%d",&tt);                p++;                if(i==j)                {                    tt=INF;                }                   e[p].u=i;                e[p].v=j;                e[p].w=tt;            }        }        sort(e+1,e+p+1,cmp);        cnt=0;        init(n);        cout<<clu()<<endl;    }    return 0;}



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脚底很厚的茧怎么办 长螨虫的脸应该怎么办 荞麦皮枕头生虫怎么办 车保养手册丢了怎么办 乳胶枕发黄掉渣怎么办 拉完头发太贴了怎么办 头发又厚又蓬松怎么办 手捻葫芦去皮后怎么办 苹果5s屏幕脱胶怎么办 夏天车内温度高怎么办 反复长粉刺痘痘怎么办 砂锅烫到手很痛怎么办 低压高怎么办吃什么好 熬夜多了掉头发怎么办 复蚕丝被洗过了怎么办 买了梅邦虫草精怎么办 医院不开转院证怎么办 棕垫一直有味道怎么办 棕子床垫味道大怎么办 羊绒大衣洗坏了怎么办 无痕内裤开胶了怎么办 衬衫洗了会缩水怎么办 脾虚引起的眼袋怎么办 沙漠玫瑰根烂了怎么办 多肉种子不发芽怎么办 多肉植物掉叶子怎么办 白色衣服染了蓝色怎么办 白色的衣服染色了怎么办 白毛衣染上金纺怎么办 白色的衣服变黄怎么办 白衣服84漂黄了怎么办 真丝的衣服皱了怎么办 衣服领子洗大了怎么办 真丝围巾洗皱了怎么办 真丝裙子洗花了怎么办 衣服晒得掉色了怎么办 衣服上出现霉点怎么办 校服后面的霉点怎么办 被子潮了有味道怎么办 涨奶堵塞有硬块怎么办 军训裤子腰大了怎么办