必做: 1041、1024、1077、2218、1183(较难)

1041 Car的旅行路线

 

2001年NOIP全国联赛提高组

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
 
 
 
题目描述 Description

又到暑假了,住在城市A的Car想和朋友一起去城市B旅游。她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第I个城市中高速铁路了的单位里程价格为Ti,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为t。

那么Car应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。
任务
找出一条从城市A到B的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。

输入描述 Input Description

第一行为一个正整数n(0<=n<=10),表示有n组测试数据。
每组的第一行有四个正整数s,t,A,B。
S(0<S<=100)表示城市的个数,t表示飞机单位里程的价格,A,B分别为城市A,B的序号,(1<=A,B<=S)。
接下来有S行,其中第I行均有7个正整数xi1,yi1,xi2,yi2,xi3,yi3,Ti,这当中的(xi1,yi1),(xi2,yi2),(xi3,yi3)分别是第I个城市中任意三个机场的坐标,T I为第I个城市高速铁路单位里程的价格。

输出描述 Output Description

共有n行,每行一个数据对应测试数据。

样例输入 Sample Input

1
3 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3

样例输出 Sample Output

47.5

数据范围及提示 Data Size & Hint

如描述

 
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
#define N 110 
struct node{
    int x,y,id;
    node(int x=0,int y=0,int id=0):x(x),y(y),id(id){}
}e[N*4];
double g[N][N];
int n,fly,T,a,b,car[N];
double dis(int p,int q){
    //return sqrt((double)(f(e[p].x-e[q].x)+f(e[p].y-e[q].y)));//sqrt不支持多重运算 
    int xx=e[p].x-e[q].x;
    int yy=e[p].y-e[q].y;
    return sqrt(xx*xx+yy*yy);
}
double dis1(int p,int q){
    int xx=e[p].x-e[q].x;
    int yy=e[p].y-e[q].y;
    return xx*xx+yy*yy;
}
node cal(int k1,int k2,int k3,int id)//处理第四个点,因为第四个点的位置不一定在哪里--要枚举下 
{
      double d12=dis1(k1,k2),d13=dis1(k1,k3),d23=dis1(k2,k3);
      if(d12==d13+d23){
            int dx=e[k3].x-e[k1].x;
            int dy=e[k3].y-e[k1].y;
            return node(e[k2].x-dx,e[k2].y-dy,id);
      }
      if(d23==d12+d13){
            int dx=e[k1].x-e[k2].x;
            int dy=e[k1].y-e[k2].y;
            return node(e[k3].x-dx,e[k3].y-dy,id);
      }
      int dx=e[k2].x-e[k1].x;
      int dy=e[k2].y-e[k1].y;
      return node(e[k3].x-dx,e[k3].y-dy,id);
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d",&n,&fly,&a,&b);
        for(int i=1;i<=n;i++){
            int k1=(i-1)*4+1,k2=k1+1,k3=k1+2,k4=k1+3;
            scanf("%d%d%d%d%d%d%d",&e[k1].x,&e[k1].y,&e[k2].x,&e[k2].y,&e[k3].x,&e[k3].y,&car[i]);
            e[k1].id=e[k2].id=e[k3].id=i;
            e[k4]=cal(k1,k2,k3,i); 
        }
        for(int i=1;i<=4*n;i++){
            for(int j=1;j<=4*n;j++){
                g[i][j]=1e6;
            }
        }
        for(int i=1;i<=4*n;i++){
            for(int j=1;j<=4*n;j++){
                if(e[i].id==e[j].id){
                    g[i][j]=min(g[i][j],dis(i,j)*car[e[i].id]);
                }
                else{
                    g[i][j]=min(g[i][j],dis(i,j)*fly);
                }
            }
        }
        for(int i=1;i<=4*n;i++) g[i][i]=0;
        for(int k=1;k<=4*n;k++){
            for(int i=1;i<=4*n;i++){
                for(int j=1;j<=4*n;j++){
                    if(i!=j&&i!=k&&j!=k){
                        g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
                    }
                }
            }
        }
        double ans=0x3f3f3f3f;
        for(int i=a*4-3;i<=a*4;i++){
            for(int j=b*4-3;j<=b*4;j++){
                ans=min(ans,g[i][j]);
            }
        }
        printf("%.1lf
",ans);
    }
    return 0;
}

1024 一塔湖图

 

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
 
 
 
题目描述 Description

       小松所在的PK大学校园又称作燕园,是一个十分美丽的校园。有博雅塔,未名湖,亚洲最大的高校图书馆,人称“一塔湖图”。但是由于燕园的历史比较悠久,所以很多的老房子都要不断地维修(就像故宫现在在维修一样),这导致了燕园中的一些路是禁止通行的。

       十分有趣的是,整个燕园的形状是南北朝向的一个四边形,而燕园的建筑格局也十分有规则。你可以假设他被n条横向的路和m条纵向的路分割成了大大小小的很多块区域。禁止通行的那些路正好在两个相邻的交叉路口之间。小松十分想知道,他要从他宿舍所在的A路口到达图书馆所在的B路口需要多少时间(他只能沿着能够通行的道路行走,并假设小松走1单位长度需要1单位的时间)?你能帮助他吗?(不要误会小松如此勤奋要去图书馆看书,小松去图书馆的主要原因是那里是全校PPMM最多的地方)。

       另外要说的是,燕园中还有很多的地方是湖。所以湖所占的区域也是不能通行的。

输入描述 Input Description

       输入文件的第一行包含4个整数n(1≤n10),m(1≤m10),t(1≤t100),k(1≤k10)。分别表示燕园中有n条纵向的路和m条横向的路,t条不能通行的路,还有k个湖。接下来的一行有n个整数a1..anai(0ai100)表示从西往东第i条纵向向路离燕园最西端的距离;再接下来的一行有m个整数b1..bmbi(0≤bi100)表示从南往北第i条横向路离燕园最南端的距离;再接下来k行,每行四个整数x1,x2,y1,y2表示由西向东的第x1条路到第x2条路和由南向北的第y1条路到第y2条路之间是一个湖。要注意的是湖的边缘是可以行走的,湖也可能有重叠,如果两个湖接壤的话,接壤的部分也是可以行走的;再接下来t行,每行4个整数x1,y1,x2,y2,表示路口(x1,y1)和(x2,y2)之间的路是静止通行的,你可以认定该两个路口一定是相邻的;最后一行包含4个整数x1,y1,x2,y2,表示小松所在的路口A(x1,y1)和图书馆所在的路口B(x2,y2)。

注:路口(xy)表示由西向东的第x条纵向路和由南向北的第y条横向路的交叉口。

输出描述 Output Description

输出包括一个整数,表示小松最少需要花费的时间。保证不会出现无解的情况。

样例输入 Sample Input

4 4 2 1

0 1 3 4

0 1 3 4

2 4 2 4

2 2 3 2

2 4 3 4

1 3 4 4

样例输出 Sample Output

11

数据范围及提示 Data Size & Hint
注意读题,哎,又要离散化--之后floyed即可AC(pass:样例有误)
#include<iostream> 
#include<cstdio>
using namespace std;
#define inf 1e6
int x[10],y[10];
int n,m,t,k,x1,y1,x2,y2;
int a,b;
int f[120][120];
int main()
{
    cin>>n>>m>>t>>k;
    for(int i=1;i<=n;i++)cin>>x[i];
    for(int i=1;i<=m;i++)cin>>y[i];
    for(int i=1;i<=n*m;i++)
        for(int j=1;j<=n*m;j++){
            if(i==j)f[i][j]=0;
            else f[i][j]=inf; 
        } 
    for(int i=1;i<=n;i++)//又是离散化 
        for(int j=1;j<=m;j++){
            if(i>1)
                f[(j-1)*n+i][(j-1)*n+i-1]=x[i]-x[i-1];
            if(j>1)
                f[(j-1)*n+i][(j-2)*n+i]=y[j]-y[j-1];
            if(i<n)
                f[(j-1)*n+i][(j-1)*n+i+1]=x[i+1]-x[i];
            if(j<m)
                f[(j-1)*n+i][j*n+i]=y[j+1]-y[j];
        }
    for(int i=1;i<=t;i++){
        cin>>x1>>y1>>x2>>y2;
        f[(y1-1)*n+x1][(y2-1)*n+x2]=inf;
        f[(y2-1)*n+x2][(y1-1)*n+x1]=inf;
    }
    for(int i=1;i<=k;i++){
        cin>>x1>>x2>>y1>>y2;
        for( a=x1;a<=x2-1;a++)
            for( b=y1+1;b<=y2-1;b++){
                f[(b-1)*n+a][(b-1)*n+a+1]=inf;
                f[(b-1)*n+a+1][(b-1)*n+a]=inf;
            } 
        for( b=y1;b<=y2-1;b++)
            for( a=x1+1;a<=x2-1;a++){
                f[(b-1)*n+a][b*n+a]=inf;
                f[b*n+a][(b-1)*n+a]=inf;
            }
    }    
    for(int k=1;k<=n*m;k++)
        for(int i=1;i<=n*m;i++)
            for(int j=1;j<=n*m;j++)
                f[i][j]=min(f[i][k]+f[k][j],f[i][j]);
    cin>>x1>>y1>>x2>>y2;
    cout<<f[(y1-1)*n+x1][(y2-1)*n+x2];
    return 0; 
}

1077 多源最短路

 

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
 
 
题目描述 Description

已知n个点(n<=100),给你n*n的方阵,a[i,j]表示从第i个点到第j个点的直接距离。        

现在有Q个询问,每个询问两个正整数,a和b,让你求a到b之间的最短路程。        

满足a[i,j]=a[j,i];

输入描述 Input Description

 第一行一个正整数n,接下来n行每行n个正整数,满足a[i,i]=0,再一行一个Q,接下来Q行,每行两个正整数a和b。

输出描述 Output Description

一共Q行,每行一个整数。

样例输入 Sample Input

3

 0 1 1

1 0 3

1 3 0

1

2 3

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint

n<=100,Q可能非常大。g[i][j]均>=0

请使用flyod算法

使用C/C++的同学请注意:由于输入数据较大,使用cin和cout会导致程序超时。请使用scanf与printf进行输入和输出。

分类标签 Tags 点此展开 

 
#include<iostream>
#include<cstdio>
using namespace std;
#define N 101
int n,m,a[N][N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    scanf("%d",&m);
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i!=j&&j!=k&&i!=k){
                    a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
                }
            }
        }
    }
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        printf("%d
",a[x][y]);
    }
    return 0;
}

2218 补丁vs错误

 

1999年CTSC国家队选拔赛

 时间限制: 1 s
 空间限制: 64000 KB
 题目等级 : 大师 Master
 
 
 
题目描述 Description

错误就是人们所说的Bug。用户在使用软件时总是希望其错误越少越好,最好是没有错误的。但是推出一个没有错误的软件几乎不可能,所以很多软件公司都在疯狂地发放补丁(有时这种补丁甚至是收费的)。T公司就是其中之一。

上个月,T公司推出了一个新的字处理软件,随后发放了一批补丁。最近T公司发现其发放的补丁有致命的问题,那就是一个补丁在排除某些错误的同时,往往会加入另一些错误.

此字处理软件中只可能出现n个特定的错误,这n个错误是由软件本身决定的。T公司目前共发放了m个补丁,对于每一个补丁,  都有特定的适用环境,某个补丁只有在当前软件中包含某些错误而同时又不包含另一些错误时才可以使用,如果它被使用,它将修复某些错误而同时加入某些错误。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。

更准确地说明:

设此字处理软件中可能出现的n个错误为集合B={b1,b2,…,bn}中的元素,T公司目前共发放了m个补丁:p1,p2,…,pm。对于每一个补丁pi,  都有特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以用,为了说明清楚,设错误集合:Bi+、 Bi-, 当软件包含了Bi+中的所有错误, 而没有包含Bi-中的任何错误时,补丁Pi才可以被使用,否则不能使用,显然 Bi+、Bi-交集为空。补丁pi将修复某些错误而同时加入某些错误,设错误集合Fi-、Fi+,使用过补丁pi之后,Fi-中的任何错误都不会在软件中出现,而软件将包含Fi+中的所有错误, 同样Fi-、Fi+交集为空。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。

现在T公司的问题很简单,其字处理软件的初始版本不幸地包含了集合B中的全部n个错误,  有没有可能通过使用这些补丁(任意顺序地使用,一个补丁可使用多次), 使此字处理软件成为一个没有错误的软件。如果可能,希望找到总耗时最少的方案。

输入描述 Input Description

输入文件第一行有两个正整数n和m,  n表示错误总数,m表示补丁总数。接下来m行给出了m个补丁的信息。每行包括一个正整数(表示此补丁程序pi的运行耗时)和两个长度为n的字符串,中间用一个空格符隔开。

第一个字符串,如果第k个字符为’+’,则表示bk属于Bi+,  若为‘-’,则表示bk属于Bi-, 若为‘0’,则bk 既不属于Bi+也不属于Bi-,即软件中是否包含bk不影响补丁pi是否可用。

第二个字符串,如果第k个字符为’+’,则表示bk属于Fi+,  若为‘-’,则表示bk属于Fi-, 若为‘0’,则bk 既不属于Fi+也不属于Fi-,即软件中是否包含bk不会因使用补丁pi而改变。

输出描述 Output Description

输出一个整数,如果问题有解,输出总耗时,否则输出0。

样例输入 Sample Input

3 3

1 000 00-

1 00- 0-+

2 0-- -++

样例输出 Sample Output

8

数据范围及提示 Data Size & Hint

1<=n<=20, 1<=m<=100

分类标签 Tags 点此展开 

 
 

详见:http://www.cnblogs.com/shenben/p/5626133.html

复制代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 1500010
#define inf 2e7
struct edge{
    int d;
    int b1,b2;
    int f1,f2;
}e[N];
int n,m,dist[N],use[N];
queue<int>q;
void spfa(int s)
{
    for(int i=0;i<=(1<<n)-1;i++) dist[i]=inf;
    dist[s]=0;
    q.push(s);
    use[s]=1;
    while(!q.empty()){
        int f=q.front(); q.pop();
        use[f]=0;
        for(int i=1;i<=m;i++){
            if(((e[i].b1&f)==e[i].b1)&&((e[i].b2&f)==0)){
                int x=f;
                for(int j=0;j<n;j++){
                    if((1<<j)&e[i].f2&&(1<<j)&x) x^=(1<<j);
                }
                x|=e[i].f1;
                if(dist[x]>dist[f]+e[i].d){
                   dist[x]=dist[f]+e[i].d;
                    if(!use[x]){
                        use[x]=1;
                        q.push(x);
                    }
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&e[i].d);
        char b[25],f[25];
        cin>>b>>f;
        for(int j=0;j<n;j++){
            if(b[j]=='+') e[i].b1|=(1<<j);
            if(b[j]=='-') e[i].b2|=(1<<j);
            if(f[j]=='+') e[i].f1|=(1<<j);
            if(f[j]=='-') e[i].f2|=(1<<j);          
        } 
    }
    spfa((1<<n)-1);
    printf("%d
",dist[0]==inf?0:dist[0]);
    return 0;
}
复制代码

1183 泥泞的道路

 

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
 
 
题目描述 Description

CS有n个小区,并且任意小区之间都有两条单向道路(a到b,b到a)相连。因为最近下了很多暴雨,很多道路都被淹了,不同的道路泥泞程度不同。小A经过对近期天气和地形的科学分析,绘出了每条道路能顺利通过的时间以及这条路的长度。

现在小A在小区1,他希望能够很顺利地到达目的地小区n,请帮助小明找出一条从小区1出发到达小区n的所有路线中(总路程/总时间)最大的路线。请你告诉他这个值。

输入描述 Input Description

第一行包含一个整数n,为小区数。

接下来n*n的矩阵P,其中第i行第j个数表示从小区i到小区j的道路长度为Pi,j。第i行第i个数的元素为0,其余保证为正整数。

接下来n*n的矩阵T,第i行第j个数表示从小区i到小区j需要的时间Ti,j。第i行第i个数的元素为0,其余保证为正整数。

输出描述 Output Description

写入一个实数S,为小区1到达n的最大答案,S精确到小数点后3位。

样例输入 Sample Input

3

0 8 7 

9 0 10 

5 7 0 

0 7 6 

6 0 6 

6 2 0

样例输出 Sample Output

2.125

数据范围及提示 Data Size & Hint

【数据说明】

30%的数据,n<=20

100%的数据,n<=100,p,t<=10000

分类标签 Tags 点此展开 

 
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define N 105
queue<int>que;
int n,p[N][N],t[N][N],vis[N],id[N];
double f[N][N],dis[N],mid;
bool spfa(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            f[i][j]=(double)p[i][j]-(double)t[i][j]*mid;
        }
    }
    memset( id,0,sizeof  id);
    memset(vis,0,sizeof vis);
    memset(dis,-0x3f,sizeof dis);
    while(!que.empty()) que.pop();
    que.push(1);
    id[1]++;
    vis[1]=1;
    dis[1]=0.0;
    while(!que.empty()){
        int h=que.front();que.pop();
        vis[h]=0;
        for(int i=1;i<=n;i++){
            if(i==h) continue;
            if(dis[i]<dis[h]+f[h][i]){
               dis[i]=dis[h]+f[h][i];
                if(!vis[i]){
                    vis[i]=1;
                    id[i]++;
                    if(id[i]>n) return 1;
                    que.push(i);
                }
                
            }
        }
    }
    return dis[n]>=0;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&p[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&t[i][j]);
    double l(0.0),r(10000.0),eps(0.0001);
    while(r-l>eps){
        mid=(l+r)/2.0;
        if(spfa())
            l=mid;
        else
            r=mid;
    }
    printf("%.3lf",mid);
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/5625826.html