长途旅行(压缩问题)

题目

【问题描述】

  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
#include
#include
#include
#include
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()
{
    queueq;
    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;
}

收获

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

你可能感兴趣的:(图论,技巧或者知识点)