【例题】【图论(哈密顿回路)&DP(状压)】

26 篇文章 0 订阅
14 篇文章 0 订阅

1、
NKOJ 3707 送外卖
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限2000ms

问题描述
有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上。n个不同的客户分别在1~n个编号的城市中。送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少。现在已知任意两个城市的直接通路的时间。

输入格式
第一行一个正整数n (1<=n<=15)
接下来是一个(n+1)*(n+1)的矩阵,矩阵中的数均为不超过10000的正整数。矩阵的i行j列表示第i-1号城市和j-1号城市之间直接通路的时间。当然城市a到城市b的直接通路时间和城市b到城市a的直接通路时间不一定相同,也就是说道路都是单向的。

输出格式
一个正整数表示最少花费的时间

样例输入
3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0

样例输出
8

来源 East Central North America 2006

思路:因为一个城市可以经过多次,所以先Floyd。

#include<cstdio>
#include<iostream> 
using namespace std;
const int need=18;

int a[need][need],f[1<<16][need];

int main()
{
    int n;scanf("%d",&n);
    int s=(1<<n+1)-1;
    for(int j,i=0;i<=n;i++)
     for(j=0;j<=n;j++)
      scanf("%d",&a[i][j]);
    for(int i,j,k=0;k<=n;k++)
     for(int i=0;i<=n;i++)
      for(int j=0;j<=n;j++)
       a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
    for(int j,i=1;i<=s;i++)
     for(j=0;j<=n;j++) f[i][j]=1e9;
    f[1][0]=0;
    int u,v,t;
    for(int i=1;i<=s;i++)
     for(u=0;u<=n;u++)
      if((i>>u)&1)
       for(v=0;v<=n;v++)
        if(!((i>>v)&1))
        {
            t=i|(1<<v);
            f[t][v]=min(f[t][v],f[i][u]+a[u][v]);
        }
    int ans=1e9;
    for(int i=1;i<=n;i++)
    {
        ans=min(ans,f[s][i]+a[i][0]); 
    }
    printf("%d",ans);
}

2、****NKOJ 1752 传球游戏
时间限制 : 10000 MS 空间限制 : 65536 KB

问题描述
n个人在做传球的游戏,编号为1-n。
  游戏规则是这样的:开始时球可以在任意一人手上,他可把球传递给其他人中的任意一位;下一个人可以传递给未接过球的任意一人。
  即球只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;
  求当球经过所有n个人后,整个过程的最小总代价是多少。

输入格式
第一行为n,表示共有n个人(16>=n>=2);
  以下为n*n的矩阵,第i+1行、第j列表示球从编号为i的人传递到编号为j的人所花费的代价,特别的有第i+1行、第i列为-1(因为球不能自己传给自己),其他数据均为正整数(<=10000)。

输出格式
一个数,为最小的代价总和。

样例输入
样例输入1:
2
-1 9794
2724 –1

样例输入2:
4
-1 4551 3763 4873
4465 -1 9323 2204
705 2102 -1 333
9264 5206 2596 -1

样例输出
样例输出1:
2724

样例输出2:
5505

思路:
因为是单向边,所以可能选择不同起点得到的最短路径不同,理论上应该统计所有以节点为起点的最小值
但可以增加虚拟节点来减少代码复杂度:把0号看做与所有点距离为零的虚拟节点,只用讨论从0出发,经过所有点的最短距离就好了。

#include<cstdio>
#include<iostream> 
using namespace std;
const int need=18;

int a[need][need],f[1<<17][need];

int main()
{
    int n;scanf("%d",&n);
    int s=(1<<n+1)-1;
    for(int j,i=1;i<=n;i++)
     for(j=1;j<=n;j++)
     {
        scanf("%d",&a[i][j]);
        if(i==j) a[i][j]=0;
     }
    for(int j,i=1;i<=s;i++)
     for(j=0;j<=n;j++) f[i][j]=1e9;
    f[1][0]=0;
    int u,v,t;
    for(int i=1;i<=s;i++)
     for(u=0;u<=n;u++)
      if((i>>u)&1)
       for(v=0;v<=n;v++)
        if(!((i>>v)&1))
        {
            t=i|(1<<v);
            f[t][v]=min(f[t][v],f[i][u]+a[u][v]);
        }
    int ans=1e9;
    for(int i=0;i<=n;i++)
    {
        ans=min(ans,f[s][i]); 
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值