luogu P1078 文化之旅

NOIP 2012 普及组 第四题
题目描述

有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一

种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。不

同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同,有些文化会排斥外来

文化(即如果他学习了某种文化,则他不能到达排斥这种文化的其他国家)。

现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这

位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求

从起点到终点最少需走多少路。

输入输出格式

输入格式:
第一行为五个整数 N,K,M,S,T,每两个整数之间用一个空格隔开,依次代表国家

个数(国家编号为 1 到 N),文化种数(文化编号为 1 到 K),道路的条数,以及起点和终点

的编号(保证 S 不等于 T);

第二行为 N 个整数,每两个整数之间用一个空格隔开,其中第 i 个数 Ci,表示国家 i

的文化为 Ci。

接下来的 K 行,每行 K 个整数,每两个整数之间用一个空格隔开,记第 i 行的第 j 个数

为 aij,aij= 1 表示文化 i 排斥外来文化 j(i 等于 j 时表示排斥相同文化的外来人),aij= 0 表示

不排斥(注意 i 排斥 j 并不保证 j 一定也排斥 i)。

接下来的 M 行,每行三个整数 u,v,d,每两个整数之间用一个空格隔开,表示国家 u

与国家 v 有一条距离为 d 的可双向通行的道路(保证 u 不等于 v,两个国家之间可能有多条

道路)。

输出格式:
输出只有一行,一个整数,表示使者从起点国家到达终点国家最少需要走的距离数(如

果无解则输出-1)。

输入输出样例
输入样例#1:
2 2 1 1 2
1 2
0 1
1 0
1 2 10
输出样例#1:
-1
输入样例#2:
2 2 1 1 2
1 2
0 1
0 0
1 2 10
输出样例#2:
10
说明

输入输出样例说明1

由于到国家 2 必须要经过国家 1,而国家 2 的文明却排斥国家 1 的文明,所以不可能到

达国家 2。

输入输出样例说明2

路线为 1 -> 2

【数据范围】

对于 100%的数据,有 2≤N≤100 1≤K≤100 1≤M≤N2 1≤ki≤K 1≤u, v≤N 1≤d≤1000 S≠T 1≤S,T≤N

解题报告
可以说题中规定有些路能走,有些路不能走——可以说是求特定规则下的最短路问题。
本来想跑SPFA,但SPFA形式于广搜,如果走过就标记的话会导致沿着这条路走下去有可能结果不是最短路或者根本无解,所以用大法师回溯,注意剪枝。
注意dfs前初始化起点的文化已经学习,否则出错。

代码如下:

#include
#include
#include
using namespace std;

bool c[101],b[101];//c:cultrue; b:in queue or not
int n,k,m,s,t,x,y,z,num_edge,sum,best=0x7f7f7f7f,cul[101],dis[101],head[101],matrix[101][101];//cul[i]第i个城市的文化,matrix文化的排斥关系 
struct Edge{
    int next,to,dis;
};
Edge edge[10001];

int read()
{
    int now=0; char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')
    {
        now=(now<<1)+(now<<3)+ch-'0';
        ch=getchar();
    }
    return now;
}

void add_edge(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}
/*spfa不能回溯 
void SPFA()
{
    memset(dis,0x7f,sizeof(dis));
    int q[101]; int front=0,tail=1;
    q[1]=s; dis[s]=0;
    c[cul[s]]=true; b[s]=true;
    do
    {
        int now=q[++front];
        b[now]=false;

        for (int i=head[now]; i!=0; i=edge[i].next)
        {
            if ((!c[cul[edge[i].to]])&&(!matrix[cul[edge[i].to]][cul[now]])&&dis[edge[i].to]>dis[now]+edge[i].dis)//需要加括号吗?!的优先级比&&高啊
            {

                dis[edge[i].to]=dis[now]+edge[i].dis;
                c[cul[edge[i].to]]=true;
                if (!b[edge[i].to])
                {
                    b[edge[i].to]=true;
                    q[++tail]=edge[i].to;
                }
            }
        }
    }
    while (front!=tail);
}*/

void dfs(int x)
{
    if (x==t)
    {
        best=min(best,sum);
        return;
    }
    if (sum>best) return;//最优性剪枝 
    for (int i=head[x]; i!=0; i=edge[i].next)
    {
        if (!c[cul[edge[i].to]])
        {
            bool flag=false;
            for (int j=1; j<=k; j++)//循环每一种文化,找出学过的文化,判断学过的文化是否冲突 
                if (c[j]&&matrix[j][cul[edge[i].to]])
                { flag=true; break; }
            if (flag) continue;
            c[cul[edge[i].to]]=true;
            sum+=edge[i].dis;
            dfs(edge[i].to);
            c[edge[i].to]=false;//回溯 
            sum-=edge[i].dis;
        }
    }
}

int main()
{
    memset(dis,0x7f,sizeof(dis));
    n=read(); k=read(); m=read(); s=read(); t=read();
    for (int i=1; i<=n; i++)
        cul[i]=read();
    for (int i=1; i<=k; i++)
        for (int j=1; j<=k; j++)
            matrix[i][j]=read();
    for (int i=1; i<=m; i++)
    {
        x=read(); y=read(); z=read();
        add_edge(x,y,z);
        add_edge(y,x,z);
    }
//  SPFA();
    c[cul[s]]=true;//必须初始化!
    dfs(s);
/*  for (int i=1; i<=n; i++)
        printf("%d ",dis[i]);*/

    if (best==0x7f7f7f7f) printf("-1");
    else printf("%d",best);
    return 0;
}

你可能感兴趣的:(dfs深搜,图论,最短路,剪枝(巨斧砍大树))