长途旅行(压缩问题)

来源:互联网 发布:雷蛇驱动有mac版吗 编辑:程序博客网 时间:2024/04/30 06:15

题目

【问题描述】

  JY是一个爱旅游的探险家,也是一名强迫症患者。
  现在JY想要在C国进行一次长途旅行,C国拥有n个城市(编号为0,1,2…,n - 1),城市之间有m条道路,可能某个城市到自己有一条道路,也有可能两个城市之间有多条道路,通过每条道路都要花费一些时间。JY从0号城市开始出发,目的地为n–1号城市。由于JY想要好好参观一下C国,所以JY想要旅行恰好T小时。为了让自己的旅行更有意思,JY决定不在任何一个时刻停留(走一条到城市自己的路并不算停留)。
  JY想知道是否能够花恰好T小时到达n–1号城市(每个城市可经过多次)。现在这个问题交给了你。若可以恰好到达输出“Possible”否则输出“Impossible”。(不含引号)。

【输入格式】  

  第一行一个正整数Case,表示数据组数。
  每组数据第一行3个整数,分别为n, m, T。
  接下来m行,每行3个整数x, y, z,代表城市x和城市y之间有一条耗时为z的双向边。

【输出格式】  

  对于每组数据输出”Possible”或者”Impossible”。

【输入样例】  

2
3 3 11
0 2 7
0 1 6
1 2 5
2 1 10000
1 0 1

【输出样例】  

Possible Impossible

【样例解释】  

第1组:0 -> 1 -> 2 :11 第2组:显然偶数时间都是不可能的。

【数据范围】  

30%的数据满足:T≤10000
另有30%的数据满足:2≤n≤5 1≤m≤100
100%的数据满足: 2≤n≤50 1≤m≤100 1≤z≤10000 1≤T≤10^18
       1≤Case≤5
      

分析

看了网上很多题解,并不是很明白,然后下定决心自己搞懂之后,觉得写一个仍旧不是很明白的题解,重要的是自己思考。

作为一个图论问题,首先要自己想一下自己会什么,比较明显的是这不是欧拉回路或者说网络流,那么就只有SPFA了(什么逻辑啊!!)
T比较大,所以说我们要想办法压缩,利用一个dp的思想—重叠子问题,意思是说第j时刻到达i点可能很很多种到达方式,如果我们能不总是重复计算,只需要到达了一次就不再计算,就达到了大量化简的目的(开玩笑这种题目不知道浪费了多少)。
首先直接给出思路:找出一个比较小的环(一般来说是自环或者0~1的来回路径),我们可以在这个环上走很多次才开始继续走,为什么这样可以达到压缩的目的呢,一个是比较数字化的等下直接从方程看待,另一个就是因为之前说过了到达的方式有很多种,说不定这就是一种,而且可能性很大!。
设这个环的大小为mod,则T=t+k*mod,当k=0的时候,一定不影响结果,所以可以这么操作。
压缩后d[i][j]表示在j时刻到达i点已经走过了的最小时间,这里的值%mod一定为j,比如mod=5,d[i][2]=12,就表示第一次走到i时间是2+k*mod的时候是12(k=2),因此接下来17、22、27…都可以通过不停地走那个环来达到。
最后来看答案d[n-1][t%mod],如果它的答案是t,说明刚好可以走到,如果是t-k*mod,那么就走几个环,所以只要d[n-1][t%mod]<=t就可以达到
至于方程的转移,实际上就是最短路问题,用SPFA,化简之处如例子:用一个状态就表达了17、22、27…能化简得少么?

代码

注意:出于个人习惯,代码中所有的点的标号都被+1

#include<queue>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;typedef long long LL;const LL inf=1e18+5;const int maxn=55,maxm=105,maxw=10000*2+5;struct edge{    int to,next,w;}E[maxm<<1];int Case,mod;int n,m,np,first[maxn];LL t;LL d[maxn][maxw];void add(int u,int v,int w){    E[++np]=(edge){v,first[u],w};    first[u]=np;}void Init(){    scanf("%d%d",&n,&m);    cin>>t;    mod=1e9;    np=0;    memset(first,0,sizeof(first));    int u1,u2,w;    for(int i=1;i<=m;i++)    {        scanf("%d%d%d",&u1,&u2,&w);        u1++,u2++;        if(u1==1||u2==1)        {            mod=min(mod,2*w);        }        add(u1,u2,w);        add(u2,u1,w);    }}struct data{    int i,k;};bool inq[maxn][maxw];void SPFA(){    queue<data>q;    for(int i=0;i<=n;i++)        for(int j=0;j<=mod;j++)            d[i][j]=inf;    q.push((data){1,0});    d[1][0]=0;    while(!q.empty())    {        data i=q.front();q.pop();        inq[i.i][i.k]=0;        for(int p=first[i.i];p;p=E[p].next)        {            int j=E[p].to,nk=(i.k+E[p].w)%mod;            if(d[j][nk]>d[i.i][i.k]+E[p].w)            {                d[j][nk]=d[i.i][i.k]+E[p].w;                if(inq[j][nk])continue;                inq[j][nk]=1;                q.push((data){j,nk});            }        }    }}void solve(){    if(mod==1e9)    {        printf("Impossible\n");        return;    }    SPFA();    if(d[n][t%mod]<=t)        printf("Possible\n");    else        printf("Impossible\n");}int main(){    //freopen("in.txt","r",stdin);    scanf("%d",&Case);    while(Case--)    {        Init();        solve();    }    return 0;}

收获

压缩问题可以从冲突上分析究竟获得了多少化简,同样的也可以通过冲突来得到压缩算法

原创粉丝点击