12.23日记
DP
- 洛谷P1280:工作日有N分钟,有K个任务,每个任务从\(p_i\)分钟开始,持续\(t_i\)分钟,每一时刻如果有多个任务要完成,则可以任选一个,但只能干一个工作,问如何选取任务,使得空暇时间最多。数据1e4。
思路:dp[i]表示i-N分钟中,最大的空闲时间。那么从后往前遍历每个任务,这个是无后效性的,与前面的任务怎么选是无关的。那么首先对任务开始时间从大到小排序,再依次从时间n-1开始dp即可。
#include
using namespace std;
const int M=1e5+20;
struct T{
int st,dur;
bool operator<(const T &x)const{
return st>x.st;
}
};
struct Task{
int n,k,dp[M];
T a[M];
void init(){
scanf("%d%d",&n,&k);
for(int i=1;i<=k;++i)
scanf("%d%d",&a[i].st,&a[i].dur);
}
void run(){
sort(a+1,a+k+1);
int p=1;
for(int i=n;i>=1;--i)
if (a[p].st!=i)
dp[i]=dp[i+1]+1;
else
while(a[p].st==i)
dp[i]=max(dp[i],dp[i+a[p].dur]),++p;
printf("%d\n",dp[1]);
}
}task;
int main(){
task.init(),task.run();
return 0;
}
CF Edu78
A. Shuffle Hashing
题意:给字符串s和h,s的hash定义为,将s随机打乱,之后加上一个任意前缀和一个任意后缀,得到的字符串即为s的hash,现询问h是否可以为s的hash。
思路:遍历所有h的长为len(s)的区间,统计各个字符个数,如果和s的各个字符个数一样,那么就是可以。时间复杂度\(O(n^2)\)。
#include
using namespace std;
const int M=1e2+50;
struct T{
int ans[28],now[28],len1,len2;
char s1[M],s2[M];
void init(){
for(int i=0;i<26;++i)
ans[i]=now[i]=0;
scanf("%s%s",s1,s2);
len1=strlen(s1),len2=strlen(s2);
}
inline bool check(){
for(int i=0;i<26;++i)
if (ans[i]!=now[i])
return false;
return true;
}
void run(){
init();
if(len2
B. A and B
题意:给两个数a和b,第k次操作为选定一个数,将其加上k。输出使得a和b相等的最少步数。
思路:求出a和b的差d,那么找到第一个x,使得x(x+1)/2>=d(也就是疯狂给那个小的数加),若d!=0,则此时a和b还是不一样。那么问题就转化为了,将x(x+1)/2分成两个数,使其相差d。这就要看x(x+1)/2+d是否为偶数,若为奇数,则还需要继续加,直到为偶数为止,这样才能分成整数。很容易理解一定加了不超过2次。
时间复杂度\(O(\log n)\)。
#include
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
struct T{
int a,b,d;
void init(){
scanf("%d%d",&a,&b);
d=abs(a-b);
}
bool check(int x){
if (1LL*x*(x+1)/2>=d)
return true;
return false;
}
void run(){
init();
int l=0,r=1e5+20;
while(l!=r){
if (check(mid))
r=mid;
else
l=mid+1;
}
while((1LL*l*(l+1)/2+d)%2==1)
++l;
printf("%d\n",l);
}
}t;
int main(){
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
t.run();
return 0;
}
C.Berry Jam
题意:给2n个罐头,一开始站在n和n+1之间,每个罐头为1或者2,可以选择吃掉i-n和n+1-j两个区间内的所有罐头。问最少需要吃掉多少个罐头,才能使得剩下的1和2的数量相等。
题解:假设1个数>2个数,那么首先计算出要额外吃掉的1的个数,记为x。之后计算的时候碰到1就+1,碰到2就-1。首先计算n往左走,每走过一个就+1/-1。可以想到,如果i处为sum,j处也为sum,而且i 时间复杂度\(O(n)\)。 D. Segment Tree 题意:给n个点,每个点表示一个区间\([l_i,r_i]\),保证所有的l,r均不重复。若两个区间有重叠但不包含,则两个点之间有边。问这个图是否是一颗树。 思路:这也算是一道数据结构题了,结果不会做……………… 区间按照l从小到大排序,每读入一个区间就将右端点和编号放入set,那么放入之前,set里剩的元素就是与之有边的元素。如果set里有右端点太小的边就删掉。显然,如果发现了超过了n-1条边,那么就直接NO。如果恰好n-1条边,再判断连通性即可。可以用并查集。 时间复杂度\(O(n\log n)\)。 明天再补吧。#include