由于搞不到原题,所以就只能叙述一下题意了。
1. n个连续的整数(1,2,3,4...n),然后要每隔m个数进行一次翻转符号,最开始是负号。保证2m整除n,比如n=8,m=2的话,这个序列就是-1,-2, 3, 4, -5, -6, 7 ,8,然后求前n项和。
题解:规律很显然,就是3+(-1) = 2, 4+(-2) = 2, 然后就是m*n/2了,但是n和m都要用long long不然会数据溢出的。
2. 长度为A的东西X个,长度为B的东西Y个,然后要组成恰好长度K的东西,不用考虑内部顺序,求一共有多少种组合方式。答案对1e9+7取模。
0<=X,Y<=100,0<K<=1000。好像是这样的数据范围。
题解:也是一道比较简单的排列组合计数题,只需要枚举A或者B,然后检测另外一个能不能恰好组成,然后就是C(X, A)+C(Y, B)了,全部加和,然后取模就是了。本来一开始以为要进行除法取模,后来一看组合数的范围只有100,那就利用组合数的加法公式进行n^2初始化就好了。C(m,n) = C(m-1,n)+C(m-1,n-1)。
注意A的总和超过K,以及B的数量是否够用这些边界情况就好了。
代码:
#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int mod = 1e9+7;
const int INF = 1<<30;
ll C[105][105];
void init( )
{
memset(C, 0, sizeof(C));
for (int i = 0; i <= 100; i ++) {
C[i][0] = 1;
for (int j = 1; j <= i; j ++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
int main( )
{
//freopen("input.txt", "r", stdin);
int K, A, X, B, Y;
scanf("%d%d%d%d%d", &K, &A, &X, &B, &Y);
ll ans = 0;
init( );
for(int i=0; i<=X; i++)
{
int remain = K-i*A;
if(remain<0 || remain%B!=0 || remain/B>Y)//can't zucheng
continue;
int num = remain/B;
ll t = (C[X][i]*C[Y][num])%mod;
ans = (ans+t)%mod;
}
cout<<ans<<endl;
return 0;
}
3. 这个题和杭电OJ的一道题一模一样,就是HDU4864了,是一个比较好的贪心题目。
题解:
(1) 笔试的时候,想的太简单了。直接对两个struct数组进行双关键字排序(时间为首,等级为次,为了收益最大),然后用两个指针分别扫任务和机器,看起来很正确,但是是错的,只过了30%。比如这样的样例,有一个任务(200, 100),机器有(300, 50),(250, 150)和(100, 200)这样的情况。无论如何这个任务都会被滤掉,不会被执行,但是事实上,这个任务是可以被完成的。因为被另外两个机器的滤掉了。
(2)发现了问题就比较好解决了,只需要分别枚举两个关键字,然后将符合第一个关键字的任务或机器加入容器中,而不是直接滤掉,然后在对第二关键字进行贪心的匹配就好了,写法感觉很多种,总之就是贪心一定要贪对。
我这里的写法是,对任务枚举,按照时间和等级降序,这样保证取到的一定是收益最大的解(因为时间的收益严格大于等级,所以时间作为第一关键字,如果能完成收益大的任务,自然不会去完成收益低的任务);然后在此基础上,加所有工作时间大于等于任务的机器加入multiset中,取出等级最接近任务等级的,且大于等于任务等级的,就是lower_bound,这样可以保证完成的任务一定是最多的,不会遗漏。(因为任务和机器都按照时间进行过排序,那么在set中机器的时间一定是可以完成后续任务的,它们是被时间更长的任务选择进来的,所以在此不需要考虑机器的工作时间了,在后面他们的时间不会有区别都是一样的,为了防止后面出现等级更大的任务,那么肯定要选择等级刚好大于任务的机器了),这样两次贪心下来得到的就是一定是最优解了,而且第二次贪心可以二分,不会超时。
(hdu里的系数是500和2,笔试题里是200和3)
代码:
#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
const int INF = 1<<30;
struct node//按照时间优先的顺序
{
int x, y;
bool operator<(const node& n) const
{
if(x != n.x)
return x>n.x;
return y>n.y;
}
}works[maxn], mars[maxn];
struct fit//在集合中选择的时候,只选择等级最接近当前的任务的机器
{
int x, y;
bool operator<(const fit& f) const
{
return y<f.y;
}
fit(int xx = 0, int yy = 0)
{
x = xx;
y = yy;
}
};
multiset<fit> st;
int main( )
{
//freopen("input.txt", "r", stdin);
int n, m;
while(~scanf("%d%d", &n, &m))
{
ll ans = 0, num = 0;
for(int i=0; i<n; i++)
scanf("%d%d", &mars[i].x, &mars[i].y);
for(int i=0; i<m; i++)
scanf("%d%d", &works[i].x, &works[i].y);
sort(mars, mars+n);
sort(works, works+m);
st.clear( );
int j = 0;
//对任务进行贪心枚举
for(int i=0; i<m; i++)
{
//第一关键字处理,优先选取时间,这样保证收益最大
while(j<n && mars[j].x>=works[i].x)
{
//将时间足够完成任务的机器全部加入set中,进行选择
st.insert(fit(mars[j].x, mars[j].y));
j++;
}
//第二关键字处理
//二分查找机器等级刚好大于任务等级的机器,这样保证能将完成的任务量最大化
if(!st.empty())
{
multiset<fit>::iterator it = st.lower_bound(fit(0, works[i].y));
if (it == st.end())
continue;
num++;
ans += 500*works[i].x + 2*works[i].y;
st.erase(it);
}
}
cout<<num<<" "<<ans<<endl;
}
return 0;
}