• 紫书 例题 11-3 UVa 1151 (有边集的最小生成树+二进制枚举子集)


    标题指的边集是说这道题的套餐, 是由几条边构成的。

    思路是先做一遍最小生成树排除边, 因为如果第一次做没有加入的边, 到后来新加入了很多权值为0的边,这些边肯定排在最前面,然后这条边的前面的那些边肯定都要再扫一遍, 也就是这条边无论如何都不会选。

    那么后来就是二进制枚举套餐, 从头开始, 加入套餐中的边然后权值加上套餐的权值, 然后把之前筛选下来的边做kruskal就ok了。

    注意要对数据范围敏感, 这里套餐最多也就8个所以可以二进制枚举子集。


    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 1123;
    int x[MAXN], y[MAXN], f[MAXN];
    int cost[MAXN], n, m, q, cnt, sum;
    struct node
    {
    	int u, v, w;
    	node(int u = 0, int v = 0, int w = 0) : u(u), v(v), w(w) {}
    	bool operator < (const node& rhs) const
    	{
    		return w < rhs.w;
    	}
    };
    vector<node> Edge, need;
    vector<int> k[9];
    
    int find(int x)
    {
    	if(f[x] != x)
    		f[x] = find(f[x]);
    	return f[x];
    }
    
    int solve()
    {
    	int ret = 0;
    	REP(i, 0, need.size())
    	{
    		int u = find(need[i].u), v = find(need[i].v);
    		if(u != v)
    		{
    			f[u] = v;
    			ret += need[i].w;
    			if(--cnt == 1) break;
    		}
    	}
    	return ret;
    }
    
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	
    	while(T--)
    	{
    		Edge.clear(); need.clear();
    		scanf("%d%d", &n, &q);
    		REP(i, 0, q)
    		{
    			int t, x;  k[i].clear();
    			scanf("%d%d", &t, &cost[i]);
    			while(t--) 
    			{
    				scanf("%d", &x);
    				k[i].push_back(x);	
    			}
    		}
    		
    		REP(i, 1, n + 1) scanf("%d%d", &x[i], &y[i]), f[i] = i;
    		REP(i, 1, n + 1)
    			REP(j, i + 1, n + 1)
    			{
    				int t = (x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) * (y[i]-y[j]);
    				Edge.push_back(node(i, j, t));
    			}
    		sort(Edge.begin(), Edge.end());
    		
    		int ans = 0; cnt = n;
    		REP(i, 0, Edge.size())
    		{
    			int u = find(Edge[i].u), v = find(Edge[i].v);
    			if(u != v)
    			{
    				need.push_back(Edge[i]);
    				f[u] = v;
    				ans += Edge[i].w;
    				if(--cnt == 1) break; //注意是1 
    			}
    		}
    		
    		REP(num, 0, 1 << q)
    		{
    			sum = 0; cnt = n; 
    			REP(i, 1, n + 1) f[i] = i;
    			REP(i, 0, q)
    				if(num & (1 << i))
    				{
    					sum += cost[i];
    					REP(j, 0, k[i].size())
    					{
    						int u = find(k[i][j]), v = find(k[i][0]);
    						if(u != v) f[u] = v, cnt--;
    					}
    				}
    			
    			sum += solve();
    			ans = min(ans, sum);
    		}
    		
    		printf("%d
    ", ans);
    		if(T) puts("");		
    	}
    	
    	return 0;
    }

  • 相关阅读:
    第十四周学习进度总结
    第十三周学习进度总结
    第十二周学习进度总结
    第十一周学习进度总结
    第十周学习进度总结
    第九周学习进度总结
    第八周学习进度总结
    day16-机器学习十讲第一讲
    day15-验证码识别
    day14-卷积网络识别手写数字
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819550.html
Copyright © 2020-2023  润新知