LightOJ - 1406 Assassin`s Creed【状压DP】

LightOJ - 1406入口

Altair is in great danger as he broke the three tenets of the assassin creed. The three tenets are: 1) never kill an innocent people, 2) always be discrete and 3) never compromise the brotherhood. As a result Altair is given another chance to prove that he is still a true assassin. Altair has to killn targets located inn different cities. Now as time is short, Altair can send a massage along with the map to the assassin's bureau to send some assassins who will start visiting cities and killing the targets. An assassin can start from any city, but he cannot visit a city which is already visited by any other assassin except him (because they do not like each other's work). He can visit a city multiple times though. Now Altair wants to find the minimum number of assassins needed to kill all the targets. That's why he is seeking your help.


Input

Input starts with an integer T (≤ 50), denoting the number of test cases.

Each case starts with a blank line. Next line contains two integers n (1 ≤ n ≤ 15) and m (0 ≤ m ≤ 50), where n denotes the number of cities and m denotes the number of one way roads. Each of the next m lines contains two integers u v (1 ≤ u, v ≤ n, u ≠ v) meaning that there is a road from u to v. Assume that there can be at most one road from a cityu to v.

Output

For each case, print the case number and the minimum number of assassins needed to kill all the targets.

Sample Input

2

 

3 2

1 2

2 3

 

6 6

1 2

2 3

2 4

5 4

4 6

4 2

Sample Output

Case 1: 1

Case 2: 2


题意:有n个(n<=15)城市,城市之间有m(m<=50)条单向路.刺客可以从任何一个城市出发去每个城市刺杀。每个刺客可以去自己去过的城市,但是不能去其他刺客去过的城市行刺,问最少需要几个刺客。

分析:状压DP,比赛时想到啦状压DP,可是子状态没有想清楚,导致wa。由于去过的城市还可以再去,走过的路还可以再走。那么在用dfs求子结构时用什么记录状态呢。

比赛时头脑有些混乱我当时记录的是每条路可以走100次(由于DFS的局限性,走完是不可能走完的),毕竟只有15个城市,但是还是wa。最后想到应该用已经访问过的点,和将访问的的点记录状态。然后就能找出能够一个刺客访问的所有城市集合。状态较多,用记忆化搜索节省时间。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
bool v[1<<16][16],used[1<<16];
int dp[1<<16],n,m,t;
vector<int> g[16];
void dfs(int x,int s)
{
    used[s]=true;
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        if(v[s][y])continue;
        v[s][y]=true;
        dfs(y,s|(1<<y));
    }
}
int dis(int s)
{
    int &res=dp[s];
    if(res!=-1)return res;
    res=200;
    for(int i=s;i>0;i=(i-1)&s)
    {
        if(used[i])
        {
            res=min(res,1+dis(s^i));
        }
    }
    return res;
}
int main()
{
    int T,x,y,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
            g[i].clear();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            x--;y--;
            g[x].push_back(y);
        }
        memset(v,false,sizeof(v));
        memset(used,false,sizeof(used));
        for(int i=0;i<n;i++)
            dfs(i,1<<i);
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        t=1<<n;
        printf("Case %d: %d\n",cas++,dis(t-1));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值