CFB. New Year and the Treasure Geolocation贪心!
这题...直接把前n行的最大的pair
加上后n行里最小的pair即可
因为最后是要归于同一个点
Codeforces 1028C rectangles
给定n个矩形,寻找其中n-1个矩形重合得到的最大矩形
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define inf 1e9 5 const int N = 132675; 6 int a[N], b[N], c[N], d[N], x[N], y[N], xx[N], yy[N]; 7 int32_t main(){ 8 9 int n; 10 cin >> n; 11 for (int i = 0; i < n;i++){ 12 cin >> a[i] >> b[i] >> c[i] >> d[i]; 13 x[i] = a[i]; 14 y[i] = b[i]; 15 xx[i] = c[i]; 16 yy[i] = d[i]; 17 } 18 sort(x, x + n); 19 sort(y, y + n); 20 sort(xx, xx + n); 21 sort(yy, yy + n); 22 23 for (int i = 0; i < n;i++){ 24 int x1 = x[n - 1]; 25 int y1 = y[n - 1]; 26 int xx1 = xx[0]; 27 int yy1 = yy[0]; 28 if(a[i]==x[n-1]) 29 x1 = x[n - 2]; 30 if(b[i]==y[n-1]) 31 y1 = y[n - 2]; 32 if(c[i]==xx[0]) 33 xx1 = xx[1]; 34 if(d[i]==yy[0]) 35 yy1 = yy[1]; 36 if(x1<=xx1&&y1<=yy1) 37 { 38 cout << x1 << " " << y1; 39 //system("pause"); 40 return 0; 41 } 42 } 43 return 0; 44 }
反对所有前缀和/线段树/---/的做法,排序+贪心就可以了,没有必要那么麻烦
三种方法,自己比较一下,就知道哪种更好了
WAY1:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define s second 5 #define f first 6 const int N = 1e6; 7 const int INF = 1e9; 8 int ans = 0; 9 pair<int,int> a[N], pre[N], suf[N];//prefix前缀,suffix后缀 10 int32_t main(){ 11 ios_base::sync_with_stdio(0); 12 cin.tie(0); 13 int n;cin>>n; 14 for(int i=1;i<=n;i++) 15 cin>>a[i].f>>a[i].s; 16 17 pre[0].f=suf[n+1].f=0;//等会用于比大小的 18 pre[0].s=suf[n+1].s=INF;//等会用于比大小的 19 for(int i=1;i<=n;i++){ 20 pre[i].f=max(pre[i-1].f,a[i].f);//表示前i个数中,最大的左端点的坐标 21 pre[i].s=min(pre[i-1].s,a[i].s);//表示前i个数中,最小的右端点的坐标 22 } 23 for(int i=n;i>=1;i--){ 24 suf[i].f=max(suf[i+1].f,a[i].f);//表示从第i个数到第n个数中,最大的左端点的坐标 25 suf[i].s=min(suf[i+1].s,a[i].s);//表示从第i个数到第n个数中,最小的右端点的坐标 26 } 27 for(int i=1;i<=n;i++){ 28 int l=max(pre[i-1].f,suf[i+1].f);//除去第i个线段后,最大的左端点的坐标 29 int r=min(pre[i-1].s,suf[i+1].s);//除去第i个线段后,最小的右端点的坐标 30 ans = max(ans, r - l); 31 } 32 cout << ans; 33 //system("pause"); 34 return 0; 35 }
WAY2:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 3e5 + 10; 4 const int INF = int(1e9); 5 #define int long long 6 multiset<int>a, b; 7 int n, l[N], r[N],ans,ans1; 8 int32_t main(){ 9 cin >> n; 10 for(int i=0;i<n;i++){ 11 cin >>l[i] >> r[i]; 12 a.insert(l[i]); 13 b.insert(r[i]); 14 } 15 for (int i = 0; i < n;i++){ 16 a.erase(a.find(l[i])); 17 b.erase(b.find(r[i])); 18 ans = max(ans, *b.begin() - *a.rbegin()); 19 a.insert(l[i]); 20 b.insert(r[i]); 21 } 22 23 cout << ans; 24 25 //system("pause"); 26 return 0; 27 }
WAY1和WAY2的思想是一样的
WAY3:和上一题是一个意思,一个是一维的,一个是二维的,显然第三种更好。
Kruskal算法
贪心策略:n个节点的最小生成树里面包含了n-1条边,这n-1条边是不可以成环的,这样就可以保证联通了,所以我们只要把这n-1条边选出来,因为是最小生成树,所以我们要找最小的权边
基本的思路:
存边
按照权值将这些边排序
排序完了开始遍历,决定把谁加入MST
判断的条件是:是否成环,成环的话就不要,不成环的话就加入MST
接下来要解决的问题就是怎么判断成环->利用并查集,共祖->成环
共祖在某种意义上就等于联通
所以有一个查的过程和并的过程
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5020; 5 const int F = 2e5 + 100; 6 struct node{ 7 int u, v, c; 8 } e[F]; 9 int f[N],cnt,sum; 10 int find(int x){ 11 return f[x] == x ? x : f[x] = find(f[x]);} 12 bool unionset(int a,int b){ 13 a = find(a),b = find(b); 14 if(a!=b){ 15 f[b] = a; 16 return true; 17 } 18 return false; 19 } 20 bool cmp(node a,node b){ 21 return a.c < b.c; 22 } 23 int main(){ 24 int n, m; 25 scanf("%d%d", &n, &m); 26 for (int i = 1; i <=m;i++){ 27 scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].c); 28 } 29 sort(e + 1, e + 1 + m, cmp); 30 for (int i = 1; i <= n;i++) 31 f[i] = i;//并查集的初始化 32 for (int i = 1; i <= m;i++){ 33 if(unionset(e[i].u,e[i].v)){ 34 cnt++; 35 sum += e[i].c; 36 } 37 if(cnt==n-1) 38 break; 39 } 40 printf("%d",sum); 41 //system("pause"); 42 return 0; 43 }
浙大校赛
ZOJ3953 intervals
过这题的人很少,但其实是一道贪心题
ternary search
这是这个贪心里需要注意的,这个三元搜索和平时说的三分搜索不一样
这个词是在codeforces global 2的E题的tag里看见的,同样是一道贪心。
要求给定区域内,不存在大于等于三个区间重合
“区间贪心”
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define scan(n) scanf("%lld", &n); 5 struct node{ 6 int l, r,id; 7 } a[50020],t[5]; 8 bool cmp(node a,node b){ 9 if(a.l==b.l) 10 return a.r < b.r; 11 return a.l < b.l; 12 } 13 bool cmp2(node a,node b){ 14 return a.r > b.r; 15 } 16 int ans[50020],n; 17 int32_t main(){ 18 int T; 19 scan(T); 20 while(T--){ 21 scan(n); 22 for (int i = 1; i <= n;i++){ 23 scanf("%lld%lld", &a[i].l, &a[i].r); 24 a[i].id = i; 25 } 26 sort(a + 1, a + 1 + n, cmp); 27 int cnt = 0; 28 t[1] = a[1]; 29 t[2] = a[2]; 30 for (int i = 3; i <= n;i++){ 31 t[3] = a[i]; 32 sort(t + 1, t + 4, cmp); 33 if(t[1].r>=t[2].l&&t[2].r>=t[3].l&&t[1].r>=t[3].l){//重合 34 sort(t+1, t + 4, cmp2); 35 ans[cnt++] = t[1].id; 36 swap(t[1], t[3]); 37 } 38 else 39 sort(t + 1, t + 4, cmp2); 40 } 41 sort(ans, ans + cnt); 42 printf("%lld\n", cnt); 43 for (int i = 0; i < cnt;i++){ 44 printf("%lld ", ans[i]); 45 } 46 } 47 //system("pause"); 48 }
贪心与动态规划的结合
这是在浙大校赛中遇到的两道题目,都是01背包,但是融入了贪心的思想
对于01背包,存在“选”与“不选”两个状态,那么选择的顺序呢?
分析这道题目的条件就会发现,需要一个简单的排序。这样计算的时候,物品体积就是排序后体积的前缀和
并且这道题目,由于有两个k值,所以定义一个二维dp数组,表示选取k1数组的前i个和k2数组的前j个
ZOJ3958
这题也是一样
观察数据,发现,Ci<=100,啊哈哈哈哈
所以Ci的和相同时,只需要看Hi的和的大小,明确了这点,小书包背上~
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 int h[600], c[600], dp[50020]; 5 int n, ans, sum; 6 int32_t main() 7 { 8 int T;cin>>T; 9 while(T--){ 10 sum = 0, ans = 0; 11 scanf("%lld", &n); 12 memset(dp, 0, sizeof(dp)); 13 for (int i = 1; i <= n;i++){ 14 scanf("%lld%lld", &h[i], &c[i]); 15 sum += c[i]; 16 } 17 //initially select all of them; 18 for (int i = 1; i <=n;i++){ 19 for (int j = sum; j >= c[i];j--){ 20 dp[j] = max(dp[j], dp[j - c[i]] + h[i]); 21 } 22 } 23 for (int i = 1; i <= sum;i++){ 24 ans = max(dp[i] * dp[i] - i * dp[i] - i * i, ans); 25 } 26 printf("%lld\n", ans); 27 } 28 //system("pause"); 29 return 0; 30 }