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 2 0 1 2 2 2 1 0 0 1 3 2 1 1 0 0 1 3 0 3
Sample Output
0 1 -1 2 0 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 1000010
using 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 505
using 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
Sample Input
4 10 20 30 40 2 1 3 2 0 2 1 1 1 0 4 100 100 100 100 1 1 3 0 2 3 2 1 3 2 1 2 7 40 10 20 10 20 80 40 4 2 3 4 5 1 4 2 0 3 5 0 2 4 5 6 4 0 1 3 6 2 0 3 2 3 4
Sample Output
40 400 160
Hint
对于第一组数据,首先拆掉第2块积木,需要20能量,然后拆掉第1块积木,需要10能量,接着拆掉第3块积木,需要10能量,最后只剩下第0块了,不需要能量了。总共需要40点能量。
对于第二组数据,无论怎么拆除,都是需要400点能量。
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 1000010
using 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
3 2 4 0 1 1 0 4 2 4 3 0 1 2 1 0 1 2 1 0
Sample Output
3 4
编程思想:根据题意,可抽象成求一颗最小生成树。这里采用利用并查集构造最小生成树的克鲁斯卡尔算法实现。
AC code:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
#define LL long long
#define MAXN 1000010
using 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;
}