Sorting It All Out(POJ 1094)
题意:不同值的升序排序序列是用小于号将元素从最小到最大排序的序列。例如,排序序列A,B,C,D意味着A< B,B < C和C < D。在这个问题上,我们会给你一套的关系形成A< B和问你确定是否已指定一个顺序。
输入:包含多组样例。每个实例第一行为两个正整数n和m,n表示要排序的对象数量,其中2 <= n <= 26。要排序的对象将是大写字母的前n个字符。m表示将在这个问题实例中给出的A < B的关系的数量。接下来是m行,每个行包含一个这样的关系,包含三个字符:大写字母、字符“<”和第二个大写字母。没有字母会超出字母表前n个字母的范围。n = m = 0表示输入结束。
输出:(1)当可以确定唯一的排列的序列时输出“Sorted sequence determined after K relations: yyyy.”K表示在输入第几组关系之后可以确定序列,yyyy代表确定的序列。
(2)当序列次序之间有矛盾时输出“Inconsistency found after K relations.”
(3)序列不能判断是否有序时输出“Sorted sequence cannot be determined.”
题解:而这三种形式的判断是有顺序的:先判断是否有环(3),再判断是否有序(1),最后才能判断是否能得出结果(2)。注意:对于(2)必须遍历完整个图,而(1)和(3)一旦得出结果,对后面的输入就不用做处理了。题目处理的情况很多,所以需要边输入边处理。
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=30;
int mapp[N][N];
int deg[N];
int temp[N];
int topsort(int n)
{
int a[N],ans=0,flag=1;
//要使用很多次入度数组,所以使用临时数组a
for (int i=1;i<=n;i++)
a[i]=deg[i];
for (int i=1;i<=n;i++)
{
int k=0,now=0;
for (int j=1;j<=n;j++)
{
if (a[j]==0)//查找入度为零的顶点个数
{
k++;
now=j;
}
}
if (k==0)
return 0;//有环
if (k>1)//不直接返回-1,是因为可能还有环没判断出来
flag=-1;
temp[ans++]=now;
a[now]=-1;
for (int j=1;j<=n;j++)
if(mapp[now][j]==1)
a[j]--;
}
return flag;
}
int main()
{
int n,m,flag;//当flag==1时,已经得出结果了
char s[5];
while (scanf("%d %d",&n,&m)&&(n||m))
{
flag=0;
memset(mapp,0,sizeof(mapp));
memset(deg,0,sizeof(deg));
for (int i=1; i<=m; i++)
{
scanf("%s",s);
if (flag)//一旦得出结果,对后续的输入不做处理
continue;
int x=s[0]-'A'+1;
int y=s[2]-'A'+1;
mapp[x][y]=1;
deg[y]++;
int cnt=topsort(n);
if(cnt==0) //有环
{
printf("Inconsistency found after %d relations.\n",i);
flag=1;
}
if(cnt==1) //有序
{
printf("Sorted sequence determined after %d relations: ",i);
for(int j=0; jprintf("%c",temp[j]+'A'-1);
printf(".\n");
flag=1;
}
}
if (!flag)
printf("Sorted sequence cannot be determined.\n");
}
return 0;
}
确定比赛名次(HDU 1285)
题意:中文题意不解释。
题解:拓扑排序模板题
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=500+7;
int mapp[N][N];
int deg[N];
void topsort(int n)
{
int k=0;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
//找到前趋为0的顶点,既入度为0的顶点,并输出
if (deg[j]==0)
{
printf("%d%c",j,i==n?'\n':' ');
deg[j]--;//去掉这个点,入度为-1
k=j;//记录这个点
break;
}
}
for (int j=1;j<=n;j++)
{
if (mapp[k][j]==1)
{
mapp[k][j]=0;//去掉以这个点为出度的边
deg[j]--;//并把边的终点的入度-1
}
}
}
}
int main()
{
int n,m;
while (~scanf("%d %d",&n,&m))
{
int u,v;
memset(mapp,0,sizeof(mapp));
memset(deg,0,sizeof(deg));
for (int i=0;iscanf("%d %d",&u,&v);
if (!mapp[u][v])//防止有重边
{
mapp[u][v]=1;
deg[v]++;//v的入读+1
}
}
topsort(n);
}
return 0;
}
最短路(HDU 2544)
题意:中文题意不解释。
题解:题意里说的很清楚,就是求A到B之间的最短路。最短路的源点是A,源点只有一个,所以用dijkstra就行,顶点个数少于100,用floyd也可以。
#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
const int N=100+7;
int v[N],dis[N],map[N][N];
int dijstra(int s,int n)
{
int ans=0,min;
memset(v,0,sizeof(v));
memset(dis,0,sizeof(dis));
for (int i=1; i<=n; i++)
dis[i]=map[s][i];
v[s]=1;
dis[s]=0;
for (int i=1; i<=n; i++)
{
min=INF;
for (int j=1; j<=n; j++)
{
if (!v[j]&&min>dis[j])
{
ans=j;
min=dis[j];
}
}
v[ans]=1;
for (int j=1; j<=n; j++)
if (!v[j]&&dis[j]>dis[ans]+map[ans][j])
dis[j]=dis[ans]+map[ans][j];
}
return dis[n];
}
int main()
{
int u,v,w;
int n,m;
while (~scanf("%d %d",&n,&m)&&(n||m))
{
memset(map,INF,sizeof(map));
for (int i=0; iscanf("%d %d %d",&u,&v,&w);
//避免重边,保留权值最小的边
if (map[u][v]>w)
map[u][v]=map[v][u]=w;
}
int coun=dijstra(1,n);
printf("%d\n",coun);
}
return 0;
}
畅通工程续(HDU 1874)
题意:中文题意不解释。
题解:求x到y之间的最短路。最短路的源点是x,源点只有一个,所以用dijkstra就行,顶点个数有200个,用floyd可能会超时。
#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
const int N=200+7;
int v[N],dis[N],map[N][N];
int x,y;
int n,m;//x表示起点,y表示终
int dijstra()
{
int ans=0,min;
memset(dis,INF,sizeof(dis));
memset(v,0,sizeof(v));
for (int i=0; imap[x][i];
v[x]=1;
dis[x]=0;
for (int i=1; i<=n; i++)
{
min=INF;
ans=0;
for (int j=0; jif (!v[j]&&min>dis[j])
{
ans=j;
min=dis[j];
}
}
v[ans]=1;
for (int j=0; jif (!v[j]&&dis[j]>dis[ans]+map[ans][j])
dis[j]=dis[ans]+map[ans][j];
}
}
return dis[y];
}
int main()
{
int u,v,w;
while (~scanf("%d %d",&n,&m)&&(n||m))
{
memset(map,INF,sizeof(map));
for (int i=0; iscanf("%d %d %d",&u,&v,&w);
//避免重边,保留权值最小的边
if (map[u][v]>w)
map[u][v]=map[v][u]=w;
}
scanf("%d %d",&x,&y);
int coun=dijstra();
if (counprintf("%d\n",coun);
else
printf("-1\n");
}
return 0;
}
Six Degrees of Cowvin Bacon(POJ 2139)
题意:一群牛,最近在拍电影,他们准备玩Six Degrees of Cowvin Bacon的游戏。每头奶牛被认为与自己的距离是0度,如果两个牛在一起演电影,则认为两头牛之间的距离为1度,如果两头牛没有在一起拍过电影,但是他们和第三头牛一起拍过电影,则两头牛之间的距离为2度,比如A和B一起拍过电影,B和C一起拍过电影,则A和B之间的距离为2度。以此类推。现在要找到哪头牛与其他牛之间平均度最小。
输入:n和m,n表示牛的头数,标号从1开始,m表示电影的部数。下面m行为每部电影参加的牛的头数以及参加电影的牛的标号。
输出:最小的平均的度数(乘以100)。
题解:这道题的题意理解起来不太好理解,看不懂题在说什么。可以换一个角度,来分析这道题。如果A和B一起拍过电影就可以认为A和B之间的距离为1;A和B一起拍过电影,B和C一起拍过电影,则可以认为A和C之间不可达,A和B之间的距离为1,C和B之间的距离为1,A和C通过点B连接,A-B-C得到A-C=3;A和B一起拍过电影,B和C一起拍过电影,C和D一起拍过电影,则B-C=3,A-D=4;以此类推。这样就可以转化成一道最短路问题,求顶点到其余点之间最短路的平均距离,最小的平均距离*100并输出。
#include
#include
#include
#include
using namespace std;
const int Max=0x3f3f3f3f;
const int N=300+7;
int v[N],dis[N],map[N][N];
void floyd(int n)
{
for (int k=1; k<=n; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
if (map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
}
int main()
{
int n,m,t,a;
scanf("%d %d",&n,&m);
memset(map,Max,sizeof(map));
while(m--)
{
memset(dis,0,sizeof(dis));
scanf("%d",&t);
for (int j=0; jscanf("%d",&a);
dis[a]=1;//标记哪些参演了电影
}
//如果两头牛同演过一部电影,则两头之间的路径长度为1,
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
if (i==j)
map[i][j]=0;
else if (dis[i]&&dis[j])
map[i][j]=map[j][i]=1;
}//所有的电影输入结束之后,map数组里存的是所有直接可达的边,既度为1的关系
//用二维数组做最短路的题,还没开始找最短路之前的map数组存的就是直接可达的两点之间的距离
floyd(n);//求的人任意两点之间的最短路
int sum;
dis[0]=0;//此时的dis数组用来存放的是顶点i到其余个点之间最短路的和
for (int i=1; i<=n; i++)
{
sum=0;
for (int j=1; j<=n; j++)
if (map[i][j]!=Max)
sum+=map[i][j];
dis[i]=sum;
}
sort(dis,dis+n+1);//排序找出最短距离之和的最小值
printf("%d",dis[1]*100/(n-1));//避免精度问题先*100再求平均值
return 0;
}
Cow Contest (POJ 3660)
题意:一群牛在比赛,每个回合可以确定两头牛之间的胜负关系。给出部分牛之间的胜负关系。保证牛与牛之间的名次不会出现矛盾。现在要给这些牛排个名次,问有几头牛的名次是可以确定的。
输入:n和m,n表示有n头牛参加比赛,m表示m个回合,下面m行,A B 表示A为获胜者。
输出:可以确定名次的奶牛的头数。
题解: floyd算法解决传递闭包问题。(A->B表示A战胜了B)假设只有三头牛,进行了两个回合的比赛,A->B,B->C,由这个回合的比赛可以得出确定的名词A->B->C,可以间接的认为A和C之间进行了比赛,转化成最短路的话就是,把每头牛看成一个顶点,每个回合的胜负关系看成一条有向路,A->B之间有路,B->C之间有路,则A->C之间有路
if (map[i][k]==1&&map[k][j]==1)
map[i][j]=1
A->B,A->C,则B与C之间的名次不能确定。
#include
#include
#include
#include
using namespace std;
const int N=110;
int c[N][N];
int coun[N];
int n,m;
void flord()
{
for (int k=1; k<=n; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
if (c[i][k]&&c[k][j])
c[i][j]=1;
}
int main()
{
int a,b;
while (~scanf("%d %d",&n,&m))
{
memset(c,0,sizeof(c));
memset(coun,0,sizeof(coun));
for (int i=0; iscanf("%d %d",&a,&b);
c[a][b]=1;
}
flord();
for (int i=1; i<=n; i++)
{
for (int j=1; j<=n; j++)
{
if (c[i][j])
{
coun[i]++;
coun[j]++;
}
}
}
int ans=0;
for (int i=1; i<=n; i++)
if (coun[i]==n-1)
ans++;
printf("%d\n",ans);
}
return 0;
}
Wormholes(POJ 3259)
题意: John 在探索自己农场的时候发现了一种神奇的虫洞,这种虫洞是单向的,这个虫洞可以回到过去,有N个农场,编号1….n,有M条路经,有W个虫洞。John想知道,从某个农场开始,走一些路和虫洞,可以回到他出发前或者见到他自己。
输入:有F组数据,每组数据有N个农场,编号1….n,有M条路经,有W个虫洞,路径是双向的,虫洞为单向的。
输出:YES或者NO。
题解:虫洞,可以回到过去,可以把虫洞看成路径长度为负数的路,这道题就成了判断途中是否存在负环的题了。Bellman ford 模板题。
#include
#include
#include
#include
using namespace std;
int n,m,w;
const int INF=0x3f3f3f3f;
const int N=5500;
struct nodenum
{
int start;
int end;
int time;
} edge[N];
int num;
void addedge(int u,int v,int w)
{
edge[num].start=u;
edge[num].end=v;
edge[num].time=w;
num++;
}
int dis[505];
int Bellman_ford()
{
for (int i=1; i<=n; i++)
dis[i]=INF;
dis[1]=0;
for (int i=1; ifor (int j=0; jif (dis[edge[j].end]>dis[edge[j].start]+edge[j].time)
{
dis[edge[j].end]=dis[edge[j].start]+edge[j].time;
}
}
}
for (int j=0; jif (dis[edge[j].end]>dis[edge[j].start]+edge[j].time)
return 0;
}
return 1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int a,b,c;
scanf("%d %d %d",&n,&m,&w);
num=0;
for (int i=1; i<=m; i++)
{
scanf("%d %d %d",&a,&b,&c);
addedge(a,b,c);
addedge(b,a,c);
}
for (int i=1; i<=w; i++)
{
scanf("%d %d %d",&a,&b,&c);
addedge(a,b,-c);
}
if (!Bellman_ford())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
Silver Cow Party(POJ 3268)
题意:有N个农场(1≤N≤1000)编号1 . .N,现在有牛从农场出发去参加大牛党在农场举X(1≤X≤N)。总共M(1<=M<=100000)条单向路,一条路的时间是Ti 。
每头母牛都必须步行去参加聚会,聚会结束后,就回到她的农场。每头奶牛都很懒,因此选择了最短时间的最佳路线。一头牛的返回路线可能与她最初的路线不同,因为道路是单向的。
求所有的奶牛中,一头奶牛走到聚会地点和回来的时间最长是多少?
输入:第1行:三个空格分隔的整数,分别是N、M和X
第2行到第M+1:线空格分隔的整数来描述道路 i : Ai、Bi 和 Ti 。农场 Ai到Bi农场,需要Ti时间。
输出:一头奶牛走到聚会地点和回来的最长时间。
题解:因为路是单向路,要求N-1个农场到X的农场的最短路不好求,要循环n-1次,时间复杂度太高了。所以可以交换map[i][j]与map[j][i]的值,把求每个农场到农场X的最短路转化成求农场x到其余农场的最短路,变成求单源最短路。
#include
#include
#include
#include
int map[1010][1010];
int dis[1010];
int logo[1010];
int n,m,x;
const int MAX=0x3f3f3f3f;
using namespace std;
void dijkstra( )
{
int now;
int min;
memset(dis,MAX,sizeof(dis));
memset(logo,0,sizeof(logo));
for (int i=1; i<=n; i++)
{
dis[i]=map[x][i];
}
dis[x]=0;
logo[x]=1;
for (int i=1; i<=n; i++)
{
min=MAX;
for (int j=1; j<=n; j++)
if (logo[j]==0&&min>dis[ j ])
{
min=dis[ j ];
now=j;
}
logo[now]=1;
for (int j=1; j<=n; j++)
if (logo[j]==0&&dis[ j ]>dis[now]+map[now][j])
dis[ j ]=dis[now]+map[now][j];
}
}
int main()
{
int a,b,t,way[1010];
scanf("%d %d %d",&n,&m,&x);
memset(map,MAX,sizeof(map));
for (int i=1; i<=m; i++)
{
scanf("%d %d %d",&a,&b,&t);
map[a][b]=t;
}
//先找到从农场X到其余农场的最短路
//既,每头牛参加完聚会回家的最短路
dijkstra( );
//把回家的最短路用另外一个数组存起来
for (int i=1; i<=n; i++)
way[i]=dis[i];
//交换map[i][j]与map[j][i]的值之后
//可以把求每个农场到农场X的最短路转化成求农场x到其余农场的最短路
for (int i=1; i<=n; i++)
for (int j=i+1; j<=n; j++)
{
t=map[i][j];
map[i][j]=map[j][i];
map[j][i]=t;
}
dijkstra( );
t=way[1]+dis[1];
//循环找最大值
for (int i=2; i<=n; i++)
if (way[i]+dis[i]>t)
t=way[i]+dis[i];
printf("%d\n",t);
return 0;
}