[TOC]
acm
标签(空格分隔): acm
贪心算法
//头文件set map
#include<set>
#include<map>
//greater头文件
#include<functional>
//按从大->小排序
multiset<int,greater<int>> bset;
multimap<int,int,greater<int>> dbmap;
//查找第一个大于等于指定值得迭代器
multimap<int,int,greater<int>> dbmap;
class map_finder
{
public:
map_finder(const int &v):value(v){}
bool operator ()(const multimap<int,int,greater<int>>::value_type &pair)
{
return pair.first >= value;
}
private:
const int value;
};
multimap<int,int>::iterator it=find_if(dbmap.begin(),dbmap.end(),map_finder(d));
//注意查找的迭代器和rbegin() rend()
51Nod 1191消灭兔子
典型的贪心,每个兔子只能射一次,所以只能用伤害值大于其血量的箭,在此前提下,箭越便宜
越好,故对兔子血量升序排列,箭对伤害值升序排列。
若i小于j 则第i支箭可以杀死的兔子,第j支箭也一定能杀死,而若j的价格小于i,就应该用j,
所以用优先数列维护即可。
#include<cstdio>
#include<cstring>
#include<cstdlib>
//#define LOCAL
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<functional>
#include<vector>
using namespace std;
const int N = 50009;
pair<int, int> p[N];
int b[N];
int main()
{
#ifdef LOCAL
freopen("1191.in","r",stdin);
freopen("1191.out","w",stdout);
#endif
int n,m;
scanf("%d%d",&n,&m);
if(m < n)
{
printf("No Solution\n");
return 0;
}
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
for(int i=0;i<m;i++)
scanf("%d%d",&p[i].first, &p[i].second);
sort(b, b+n);
sort(p, p+m);
priority_queue<int> q;
int i = 0, j = 0;
long long ans = 0;
//此处经典,先把p[j].first加入优先级队列,通过比较,看p[j]能否杀死第[i]只兔子
//如果不能杀死,就从优先级队列中去掉一个优先级最高的(注意p[j]能杀死第i只以前
//的所有兔子),注意此时该值不一定等于
//p[j].fisrt
while(j < m)
{
ans += p[j].second;
q.push(p[j].second);//价值越大,优先级越高,越出的快
if(p[j].first >= b[i] && i < n)
{
++i;++j;
}
else
{
ans -= q.top();
q.pop();
++j;
}
}
if(i < n)
printf("No Solution\n");
else
printf("%lld\n",ans);
return 0;
}
51 Nod 1065 最小正子段和
参考
思路:利用前缀和,注意如果使用tmp = p[i].first – p[i-1].first 结果错误,原因不知
#define LOCAL
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
const int N = 50009;
pair<long long, int> p[N];
int main()
{
#ifdef LOCAL
freopen("1065.in","r",stdin);
freopen("1065.out","w",stdout);
#endif
int N;
long long ans=0,llv;
p[0].first = 0 ;
p[0].second = 0 ;
cin >> N ;
for(int i = 1;i <= N;i++)
{
cin>>llv;
ans = ans + llv ;
p[i].first = ans ;
p[i].second = i ;
}
sort(p,p+N+1);
long long tmp;
int flag = 0;
for(int i = 1 ; i <= N ; i++)
{
if((p[i].first>p[i-1].first>0)&&(p[i].second>p[i-1].second))
{
if(flag == 0)
{
flag = 1 ;
ans = p[i].first-p[i-1].first;
}
else
{
if(p[i].first-p[i-1].first < ans)
ans = p[i].first-p[i-1].first ;
}
}
}
cout << ans << endl;
return 0;
}
51Nod1449 砝码称重
现在有好多种砝码,他们的重量是 w0,w1,w2,… 每种各一个。问用这些砝码能不能表示一个重量为m的东西。
样例解释:可以将重物和3放到一个托盘中,9和1放到另外一个托盘中。
思路:转换成w进制,m能否表示为w进制的数:
$$m=k0w^0+k1w1+k2*w2+k3w^3+….+knw^n$$
其中k0 ……. kn (k=-1,0,1) ,则w能被表示上述形式。
关于求余数的运算:
根据取余数的定义,如果m和n是整数且n非0,则表达式(m/n)*n+m%n的求值结果与m相等。隐含的意思是,如果m%n不等于0,则它的符号和m相同。C++语言的早期版本中允许m%n的符号匹配n的符号,而且商项负无穷一侧取整,这一方式在新标准中已经被禁止使用了。除了-m导致溢出的特殊情况,其他时候(-m)/n和m/(-n)都等于-(m/n),m&(-n)等于(-m)%n等于-(m%n)
#include <iostream>
using namespace std;
int w, m;
int main()
{
scanf("%d%d", &w, &m);
while (m)
{
if (m%w == 1 || m%w == 0)
{
m = m / w;
}
else if(m%w==w-1)
{
m = m / w + 1;
}
else
{
puts("NO");
return 0;
}
}
puts("YES");
return 0;
}
51Nod 1288汽油补给
- 这个题目一开始一点思路都没有,后来想每次到加油站都将油箱试图加满,如果里面的油都是比自己便宜的,不加。每次都替换油箱中比自己贵的油,然后跑完了一段再算之前的钱。即油先跑着,钱后算账。
把油箱里面的油按价钱分成相应的部分。每段路程车都是满油启动。
#pragma warning(disable:4996)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
struct no
{
ll price;
ll num;
bool operator<(const no & n1)
{
return price < n1.price;
}
};
const int maxn = 100005;
ll n, t;
ll dis[maxn], pri[maxn];
void input()
{
int i, j, k;
scanf("%lld%lld", &n, &t);
for (i = 1; i <= n; i++)
{
scanf("%lld%lld", &dis[i], &pri[i - 1]);
}
}
void solve()
{
int i, j, k;
vector<no>qu;
int flag = 1;
ll res = 0;
for (i = 0; i <= n; i++)
{
if (t < dis[i + 1])flag = 0;
if (flag == 0)continue;
if (i == 0)
{
no n1;
n1.price = pri[0];
n1.num = t;
qu.push_back(n1);
}
else
{
sort(qu.begin(), qu.end());
ll di = 0;
ll rmin = 0;
while (true)
{
if (di + qu[0].num >= dis[i])
{
rmin = rmin + (dis[i] - di)*(qu[0].price);
if (di + qu[0].num >= dis[i])
{
qu[0].num = qu[0].num - (dis[i] - di);
}
if (qu[0].num == 0)
{
qu.erase(qu.begin());
}
break;
}
else
{
di = di + qu[0].num;
rmin += qu[0].num*qu[0].price;
qu.erase(qu.begin());
}
}
res += rmin;
ll sum = 0;
for (k = 0; k < qu.size(); k++)
{
if (pri[i] > qu[k].price)
{
sum += qu[k].num;
}
else
{
no n1;
n1.num = t - sum;
n1.price = pri[i];
qu.erase(qu.begin() + k, qu.end());
qu.push_back(n1);
sum = t;
break;
}
}
if (sum < t)
{
no n1;
n1.price = pri[i];
n1.num = t - sum;
qu.push_back(n1);
}
}
}
if (flag == 0)
{
puts("-1");
}
else
{
printf("%lld", res);
}
}
int main()
{
// freopen("i.txt","r",stdin);
// freopen("o.txt","w",stdout);
input();
solve();
return 0;
}
动态规划
51Nod 1049 最大子段和
endmax=answer=A[0];
for(i=1;i<N;i++)
{
endmax=max(endmax,0LL)+A[i];
answer=max(answer,endmax);
}
- 说明:假设现在遍历到i处:endmax表示前i-1项的最大字段和,如果前i-1项的字段和为负数,那就抛弃前i-1项,从第i项开始算。(因为对最大字段和来说,加上一个负数,还不如不加,从当前开始算)
#define LOCAL
#include<iostream>
#include<cstring>
#include<algorithm>
#include<climits>
const int maxn=50005;
using namespace std;
int main(void)
{
#ifndef LOCAL
freopen("data2.in","r",stdin);
freopen("data2.out","w",stdout);
#endif
long long A[maxn],N,i;
long long endmax,answer;
cin>>N;
for(i=0;i<N;i++)
{
cin>>A[i];
}
endmax=answer=A[0];
for(i=1;i<N;i++)
{
endmax=max(endmax,0LL)+A[i];
answer=max(answer,endmax);
}
cout<<answer<<endl;
return 0;
}
51Nod最大字段和
//#define LOCAL
#include<iostream>
const int maxn=505;
const int maxm=505;
using namespace std;
int main(void)
{
#ifdef LOCAL
freopen("最大子矩阵和.in","r",stdin);
freopen("最大子矩阵和.out","w",stdout);
#endif
int M,N;
long long v[maxn][maxm],c[maxn],endmax,answer,ans;
int i,j,k;
cin >> M >> N ;
for(i = 1 ;i <= N ; i ++)
for(j = 1 ; j <= M ; j ++)
cin >> v[i][j] ;
c[0] = v[1][1] ;
ans = v[1][1];
for(i = 1 ; i <= M ; i ++ )
{
for(j = i ; j <= M ; j++)
{
//处理第j列
for(k = 1 ; k <= N ; k ++)
{
c[k] = (j == i) ? v[k][j] : (c[k] + v[k][j]);
}
// 动态规划,求最大字段和
endmax = answer = c[1] ;
for (k = 2 ; k <= N ; k ++)
{
endmax = max (endmax,0ll) + c[k];
answer = max (answer,endmax);
}
if (ans < answer)
ans = answer ;
}
}
cout << ans << endl ;
return 0;
}
51Nod 1050 循环数组最大子段和
解释
非常灵活的思想
#define LOCAL
#include<iostream>
const int maxn=50005;
using namespace std;
int main(void)
{
#ifdef LOCAL
freopen("循环数组最大字段和.in","r",stdin);
freopen("循环数组最大字段和.out","w",stdout);
#endif
int N;
long long s[maxn];
long long endmin,ansmin,endmax,ansmax,sum;
int i;
cin >> N ;
sum = 0 ;
for(i = 0 ;i < N ; i ++)
{
cin >> s[i] ;
sum += s[i] ;
}
endmin = ansmin = s[0];
endmax = ansmax = s[0];
for(i=1;i<N;i++)
{
endmin = min(endmin,0LL) + s[i];
ansmin = min(ansmin,endmin);
endmax = max(endmax,0LL) + s[i];
ansmax = max(ansmax,endmax);
}
cout << max(ansmax,sum-ansmin) << endl;
return 0;
}
51Nod 1086背包问题
思路:转换成二进制,第i件物品有c[i]个,每件体积为w[i],价值为p[i]
$$c[i]=k02^0+k121+k2*22+k32^3+….+kn2^n$$
其中k0 ……. kn (k=0,1) ,则 c[i]能被表示上述形式。
for(k = 1 ; k <= c[i] ;k *= 2)
{
tmpw = k * w[i] ;
tmpp = k * p[i] ;
for(j = W;j >= tmpw ; j--)
{
f[j]=max(f[j],f[j-tmpw]+tmpp);
}
c[i] = c[i] - k ;
}
for(k = W ; k >= w[i] * c[i] ; k --)
f[k] = max(f[k],f[k-w[i] * c[i]] + p[i] * c[i]);
#define LOCAL
#include<iostream>
#include<cstring>
const int MAXN=101;
const int MAXW=50001;
using namespace std;
int main(void)
{
#ifdef LOCAL
freopen("多重背包问题.in","r",stdin);
freopen("多重背包问题.out","w",stdout);
#endif
int N, i, j, W;
int f[MAXW];
int w[MAXN],p[MAXN],c[MAXN];//体积,价值,数量
int tmpw,tmpp;
cin >> N >> W;
for(i = 1 ; i <= N ; i ++)
{
cin >> w[i] >> p[i] >> c[i];
}
memset(f,0,sizeof(f));
int k ;
for(i = 1 ; i <= N ; i++)
{
for(k = 1 ; k <= c[i] ;k *= 2)
{
tmpw = k * w[i] ;
tmpp = k * p[i] ;
for(j = W;j >= tmpw ; j--)
{
f[j]=max(f[j],f[j-tmpw]+tmpp);
}
c[i] = c[i] - k ;
}
for(k = W ; k >= w[i] * c[i] ; k --)
f[k] = max(f[k],f[k-w[i] * c[i]] + p[i] * c[i]);
}
cout << f[W] << endl ;
return 0;
}