[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;
}