简单算法(贪心,模拟,乱搞)小结

P1016 旅行家的预算 - 贪心
貌似贪心基本上都得加个排序
既然要求花费最少的话,显然在收费低的那个加油站多加点油是最好的
那么你考虑这么几种情况
1.一种最理想的情况,你在每个加油站加油,都能去到另一个更低价格的加油站,那么你每次找能到达且最小价格的加油站跑过去,然后加一定升数的油,这个一定升数能刚好满足从这个加油站走到下一个“能到达且价格最低的加油站”,所以每到一个加油站,油箱内正好没油
2.你在目前加满油的情况下找不到一个能到达比你价格还小的加油站,那么此时最好加满油,走到一个能到达且价格最低的加油站,再考虑之后的事,你加满油去一个价格相对低一点的,总能省一点钱
3.目前加满油可以直接跑到终点,一种情况是能找到比你价格还低的加油站,即使目前油够用,根据上面的过程,我仍然是先走到那个加油站,不过因为油够用所以不会加油,油不够也会按最优的情况加上;另一种情况是油够用但是找不到价格更低的,还有一种情况是目前的油不够用,这两种都继续2的过程就好了

四舍五入的话。。。先加上0.005,然后乘100转int,int再转成double,再除以100就好了

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
typedef long long ll;
int n,m,pos;
double ans,d1,c,d2,p,pi[MAXN],dis[MAXN],nowc;
bool flg_end = false;
void print(double k) {
	int temp = (k + 0.005) * 100;
	k = (double)temp / 100;
	printf("%.2lf\n", k);
}
int find(int pos) {
	double far = c * d2;
	int id = pos + 1, nxt = 0;
	while(dis[pos] + far >= dis[id] && id <= n + 1) {
		if((!nxt || pi[id] <= pi[nxt]) && id != n + 1) nxt = id;
		id++;
	}
	if((id == n + 2) && (pi[nxt] >= pi[pos] || !nxt)) return -2;
	if(nxt) return nxt;
	return -1;
}
int main() {
	cin >> d1 >> c >> d2 >> p >> n;
	pi[0] = p;
	for(int i=1; i<=n; i++) {
		cin >> dis[i] >> pi[i];
	}
	dis[n+1] = d1;
	bool flg = false;
	while(1) {
		int nxt = find(pos);
		if(nxt == -1) {
			flg = true;
			break;
		}
		if(nxt == -2) {
			double tmpc = (d1 - dis[pos]) / d2;
			if(nowc >= tmpc) break;
			else {
				ans += (tmpc - nowc) * pi[pos];
				break;
			}
		}
		double tmpc = (dis[nxt] - dis[pos]) / d2;
		if(pi[nxt] < pi[pos]) {
			if(nowc >= tmpc) nowc -= tmpc;
			else {
				ans += (tmpc - nowc) * pi[pos];
				nowc = 0;
			}
		} else {
			ans += (c - nowc) * pi[pos];
			nowc = c - tmpc;
		}
		pos = nxt;
		
	}
	if(flg) {
		printf("No Solution");
	} else {
		print(ans);
	}
	return 0;
}

P1021 邮票面值设计 - 搜索 + dp判断上界(可行性dp)

规律?不存在的。这题有毒。。。不搜索没得做(至少我找到的规律基本都有问题)
问题是我怎么确定下一种票价值是多少,首先肯定的是一定比上一种票大1,其次是至多不超过“目前能组成的最大值+1”,仔细想想若超过那么最大值+1本身便无法组成
你还得加个小背包。。。设f[i]表示组成价值i至少需要多少张邮票,当超过n时剪枝
不然的话你怎么知道这种票到底对不对。。。或者说你怎么知道目前能组成到多大的面值。。。总不能无限加上去吧。
每次改变方案后,重新做一次背包做“快速检验”,求得目前能组成的最大值(一定也是连续的),更新答案与方案

然后有个很容易出错的地方
我搜索的时候把进行下一次dfs放到了if(num > ans)里面,实际上做了个假的“最优性剪枝”,因为目前组成最大面值不能更新答案不代表以目前的这个方案为基础拓展出的其他方案不能更新答案,所以应该全搜一遍,不能无端地给进入搜索加限制,即在搜索外面乱套if什么的,有剪枝操作当然可以这么做,但是一旦写错了是很难查出来的。。。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,pla[MAXN],ans,tot,f[MAXN],k,tmp[MAXN];
void dfs(int x, int now) {
	if(x == k + 1) return;
	memset(f, 0x3f, sizeof(f));
	f[0] = 0;
	tmp[x] = now;
	int num = 0;
	for(num; ; num++) {
		for(int i=1; i<=x; i++) {
			if(num >= tmp[i])
				f[num] = min(f[num], f[num-tmp[i]] + 1);
		}
		if(f[num] > n) break;
	}
	if(--num > ans) {
		ans = num;
		for(int i=1; i<=x; i++) 
			pla[i] = tmp[i];
	}
	for(int i=pla[x-1]+1; i<=num+1; i++) 
		dfs(x+1, i);
}
int main() {
	scanf("%d%d", &n, &k);
	pla[1] = 1;
	for(int i=2; i<=min(n,k); i++) {
		tot++;
		pla[i] = tot * n; 
	}
	dfs(1, 1);
	for(int i=1; i<=k; i++) {
		printf("%d ", pla[i]);
	}
	printf("\n");
	printf("MAX=%d", ans);
	return 0;
}

P2945 [USACO09MAR]沙堡Sand Castle - 贪心

直接排序做一下

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,x,y,m[MAXN],b[MAXN],ans,now;
int main() {
	scanf("%d%d%d", &n, &x, &y);
	for(int i=1; i<=n; i++) {
		scanf("%d%d", &m[i], &b[i]);
	}
	sort(m+1, m+n+1);
	sort(b+1, b+n+1);
	for(int i=1; i<=n; i++) {
		if(b[i] > m[i]) {
			ans += x * (b[i]-m[i]);
		} else {
			ans += y * (m[i]-b[i]);
		}
	}
	printf("%d\n", ans);
	return 0;
}

P2926 [USACO08DEC]拍头Patting Heads - 约数个数和

快速求因数个数
N*sqrt(N),明显会炸,然而数据水
开个桶什么的记一下数,注意删除自身的影响后再加回来(cnt[k]--/.../cnt[k]++)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int MAXNUM = 1000000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,m,cnt[MAXNUM],a[MAXN];
int try_div(int k) {
	int rtk = sqrt(k);
	int sum = 0;
	cnt[k]--;
	for(int i=1; i<=rtk; i++) {
		if(k % i == 0) {
			int ot = k / i;
			sum += cnt[i];
			if(ot > rtk) sum += cnt[ot];
		}
	}
	cnt[k]++;
	return sum;
}
int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) {
		scanf("%d", &a[i]);
		cnt[a[i]]++;
	}
	for(int i=1; i<=n; i++) {
		int tmp = a[i];
		printf("%d\n", try_div(a[i]));
	}
	return 0;
}

P1102 A-B数对 - 条件转化 - 二分
这道题体现了一个文化课和oi都用的到的方法:转化条件(文化课更常见一点)

从一个更高的角度看题,题无非是在考察一些能力,其中包含敏锐的观察力,也就是说给你一个很没道理的条件你能看出一些联系来,这就是条件的转化,思维不灵活的人尤其要注意这一点,不仅是题目中明显给出的条件转化,也可以说各种东西的转化(比如答案的转化),总之就是一条路走不通的时候试着找找别的路径
不知道怎么转化或者说忘了转化?不会做的时候就多探索,多玩一下式子,不断给他变形,加辅助量什么的,总之就是自由地探索探索性质

a - b = c,考虑到c为常数,而ab均为变量,枚举a b势必难以优化,如果转化为a = b + c,则对于一个枚举的数a,可以去找所有等于b + c = a的数字,这样还是n^2的,但是可以优化了,因为找的是相同数字,而求数对不需要考虑顺序,把他们排个序直接二分找到这个相同数字区间的左右端点就好了

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 200000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,m,c;
int a[MAXN];

ll ans;
int main() {
	scanf("%d%d", &n, &c);
	for(int i=1; i<=n; i++) {
		scanf("%d", &a[i]);
	}
	sort(a+1, a+n+1);
	for(int i=1; i<=n; i++) {
		int l = lower_bound(a+1, a+n+1, a[i]+c) - a;
		int r = upper_bound(a+1, a+n+1, a[i]+c) - a;
		ans += r - l;
	}	
	printf("%lld", ans);
	return 0;
}

P1638 逛画展 - 尺取法
注意初始值l = 1, r = 0,表示目前没有元素被考虑到,这个初始值在莫队算法里面也能见到
r是先增加后统计,l是先统计后增加
一个是++r,一个是l++
因为++r的话,退出while循环的时候,r就是现在的右端点,而若是r++,现在的右端点就得是r-1,因为是先统计后增加的
而l的话,应该先删除l的贡献,再把左端点右移
所以代码里面就是l <= n 和 r < n

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 1000000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,m,c,ans=INF,now,l_ans,r_ans;
int a[MAXN],cnt[MAXN];
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++) {
		scanf("%d", &a[i]);
	}
	int l = 1, r = 0;
	while(l <= n) {
		while(now < m && r < n) {
			r++;
			if(!cnt[a[r]]) now++;
			cnt[a[r]]++;
		}
		if(now < m) break;
		if(r - l + 1 < ans) {
			l_ans = l, r_ans = r;
			ans = r - l + 1;
		}
		cnt[a[l]]--;
		if(!cnt[a[l]]) now--;
		l++;
	}
	printf("%d %d\n", l_ans, r_ans);
	return 0;
}

P1028 数的计算
啊这道题,我居然不加记忆化!!!
事实证明没法算复杂度的搜索能记忆化一定要记忆化啊!!!
int型dfs递归边界返回0
并且注意每个dfs下面一定要写一个return f[x]
你定义了这个函数有返回值,总不能最后不返回任何值吧?

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,ans,f[MAXN];
int dfs(int x) {
    if(f[x]) {
        return f[x];
    }
    if(x == 1) return 0;
    for(int i=1; i<=x/2; i++) {
        f[x]++;
        f[x] += dfs(i);
    }
    return f[x];
}
int main() {
    scanf("%d", &n);
    dfs(n);
    printf("%d", f[n] + 1);
    return 0;
}  
posted @ 2018-10-30 20:24  Zolrk  阅读(131)  评论(0编辑  收藏  举报