POJ3229

来源:互联网 发布:药品数据库 编辑:程序博客网 时间:2024/06/05 18:06
题意:给出一个n(n<=15)个节点的带权无向图,边权为从经过此边的时间,点权为在此点停留的时间。给出m个点(m<=n),这些点必须经过。起点为1号点,且终点必须回到1号点。给出T,求在T时间内满足题意的路径上经过(且停留)的最多的点的数目。
分析:这是一道图上的状压DP。已经游览的每个点用1表示,未游览的用0表示。某时刻的游览状态就可以用一个整数的二进制形式表示,这是采用状压的原因。定义状态f(i,j)为走到第i个点时,走过的点的状态为j时花费时间的量。则f(i,j)=min(f(i,j),f(k,j-(1<<i))+dis(k,i)+rest(i))。解释:i,k为j状态路径上的点,f(k,j-(1<<i))+dis(k,i)+rest(i)表示走到k点,j的上一个状态下的时间+k走到i的时间+在i休息的时间。枚举k不断更新i可以得到最优的f(i,j),枚举i,j可以出解。
    注意:国人装B用英语出题,坑死我了。第一,经过一个点,不一定要在这停,当然这个点就不被算进ans;第二,题目叙述有个地方有问题,用红色的改正了;第三,这个人一天花12小时旅游,但是如果他要花费20小时在路上,那么这要花费他一天多旅游的时间,如果他欣赏风景到一半当天的12小时结束了他明天会继续欣赏(卧槽你睡觉的时候不会赶车啊)。
    注意:状压节约内存,所以要从0编号,他给的节点序号全部-1;还有里面有除法,除不尽,用double存。
    下面给出代码
#include<algorithm>#include<cstdio>using namespace std;const double inf = 1e9;int n, m, d, st = 0,ans = -1,u,v,dis,op;double f[16][1<<16],a[16],map[16][16];//a存点权,map存边权int main(){    while(scanf("%d%d%d",&n,&m,&d) &&(n||m||d) )    {        int temp;        double day = d * 12.0;        st = 0,ans = -1; //st表示满足题意的状态        for(int i = 0; i < n; ++i) //初始化        {            for(int j = 0; j < 1<<n; ++j)                f[i][j]=inf;            for(int j = 0; j < n; ++j)                map[i][j]=inf;            map[i][i]=0.0;        }        for(int i = 0; i < m; ++i)        {            scanf("%d",&temp) ;            st += 1<<(temp-1) ; //注意代码实现 注意代码风格        }        for(int i = 0; i < n; ++i) scanf("%lf", &a[i]) ;        double t;        while(scanf("%d%d%d%d",&u,&v,&dis,&op) &&(u||v||dis||op))        {            u--,v--; //节点号-1            t = dis*1.0;            if(op) t /= 120.0;            else t /= 80.0;            map[u][v] = map[v][u] = min(map[u][v], min(map[v][u], t)) ; //注意要有这句话,从a到b可能bus和train都通        }        for(int k = 0; k < n; ++k)        for(int i = 0; i < n; ++i)            if(i != k)                for(int j = 0; j < n; ++j)                    if(j != k)                        map[i][j] = min(map[i][j],map[i][k]+map[k][j]) ;        for(int i = 1; i < n; ++i)  //初始化1的邻接点的f值            f[i][1<<i]=map[i][0]+a[i];        for(int j = 0; j < 1<<n; ++j)  //dp            for(int i = 0; i < n; ++i)            {                if(j&(1<<i) &&(1<<i) !=j) //dp的边界条件                    for(int k = 0; k < n; ++k) if(j&(1<<k) &&(1<<k) !=j&&i != k)  //dp的边界条件                            f[i][j]=min(f[i][j],f[k][j-(1<<i) ]+map[k][i]+a[i]) ;                if((j&st) == st && f[i][j]+map[i][0] <= day) //如果满足题意                {                    int temp = j,cnt = 0;                    while(temp) //统计游览了几个景点                    {                        if(temp&1) cnt++;                        temp >>= 1;                    }                    ans = max(ans, cnt) ;                }            }        if(ans >= 0) printf("%d\n",ans) ;        else printf("No Solution\n") ;    }}


0 0