DFS ACM Battle(巧妙爆搜)

题目:

(https://ac.nowcoder.com/acm/problem/14122)

古往今来的各种传说中存在着很多魔法阵,它们的激活方式也各不相同。传说在北师大电子楼前的小花园里就有一个魔法阵,上次出现还是在很多很多很多年前,但是现在它又出现了! 这时,小Q同学面无表情地说:“传说这个魔法阵都会在都会在来自远古的恶魔Awesome Crystal Monster(ACM)降临的时候出现,现在,这个时候终于要到来了吗!”话音未落,小Q同学已经走到了魔法阵前,取出一个小瓶,开始用瓶中的圣水激活这个魔法阵,“你们快退后,这里就交给我了!” 这个魔法阵由一些点和一些连接这些点的边构成,当小Q同学把圣水滴在一个点上时,和这个点相连的所有边就会被点亮并发出神圣的光芒,当魔法阵所有的边都点亮的时候,就会出现强大的神圣力量。但是小Q同学拥有的圣水非常有限,只有10滴,现在小Q同学想知道他最少可以用多少滴圣水点亮整个魔法阵,如果耗尽圣水也不能点亮,就只能打出一波GG了。

输入描述:

第一行包含一个正整数T(≤ 20),表示测试数据的组数, 对于每组测试数据, 第一行是两个整数n(1 ≤ n ≤ 1000)和m(1 ≤ m ≤ 2000),表示魔法阵的点数和边数, 接下来m行,每行包含两个整数u,v(1 ≤ u,v ≤ n),表示有一条边连接了u号点和v号点。

输出描述:

对于每组测试数据,如果使用不超过10滴圣水可以点亮整个魔法阵,输出最少需要的圣水滴数,否则输出"GG"(不含引号)。

分析&回顾:

  首先,这道题我又没做出来(哎,被自己菜到了,加油!)。一开始我想这只是存个图,然后遍历点,所以我用的是Bfs,但发现用Bfs时没有明确的起点、终点,貌似拓展不起来~接着,我就想直接统计每个点的临边数,从大到小排序,然后依次滴,But这个思路刚冒出就打消了,因为尽管有些点的连通边多,但可能之前的药水其实会影响它的“有效”连通边

  其实,这道题有些类似下棋,赢得方法就是尽快铺满棋盘。对于这种起点、终点不确定的找最优解的搜索应该用Dfs,找到每种点滴方法的结果并筛选。因为,连通的点,相互影响,更好的方法是遍历边,边只要其中一个端点被滴到,那么这条边就相当于被遍历过(这个思路是在其他博客看到的,好巧妙啊~)。可以自己画个草图试试,这样子理解更深。

  结构体来存储每条边的两个端点,vis数组标记被点滴到的点(间接反映哪些边被标记),Dfs从任意一边开始都行(就直接按照题目输入的顺序了),不需要遍历循环来假设某个边为起始(DFS主要利用迭代,若再加上循环复杂度会很大,尽量避开:BFS-循环,DFS-迭代)。

  这是我自己画的一个图,假设自己在DFS取最优:

OK,上代码:

 1 #include <iostream>    //遍历边
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn=1000,maxm=2000,Judge=10;
 6 const int Inf=0x7fffffff;
 7 struct Node{
 8     int x,y;
 9 }node[maxm];//存无向边
10 bool vis[maxn]={0};//标记点
11 bool judge[maxm]={0};//标记边
12 int n=0,m=0,ans=Inf;
13 void Dfs(int sum,int cnt)// 覆盖的边数,使用的圣水数
14 {
15     if(cnt>Judge) return ;//超限,剪枝
16     if(sum==m){//完全覆盖
17         ans=min(ans,cnt);
18         return ;
19     }
20     if(vis[node[sum].x]==1||vis[node[sum].y]==1){//端点被访问
21             Dfs(sum+1,cnt);
22     }
23     else{//任取其中一个端点,滴圣水
24         vis[node[sum].x]=1;
25         Dfs(sum+1,cnt+1);
26         vis[node[sum].x]=0;
27             
28         vis[node[sum].y]=1;
29         Dfs(sum+1,cnt+1);
30         vis[node[sum].y]=0;
31     }    
32     return;
33 }
34 int main()
35 {
36     ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); //解绑,提高效率
37     int t;
38     cin>>t;
39     while(t--)
40     {
41         memset(vis,0,sizeof(vis));
42         memset(judge,0,sizeof(judge));
43         ans=Inf;
44         cin>>n>>m;
45         for(int i=0;i<m;++i)
46             cin>>node[i].x>>node[i].y;//存储边
47         Dfs(0,0);
48         if(ans<=10) cout<<ans<<endl;
49         else cout<<"GG"<<endl;
50     }
51     return 0;
52 }

 


posted @ 2019-02-15 16:23  Bankarian  阅读(367)  评论(0编辑  收藏  举报