最近感觉状态好多了...做cf的构造题,几乎都会(自信)今天刷了6道构造,就一题没出来..
其他的都出来了并且都是一发ac..我太强了...
好像2个1400,1个1500,3个1600
就一个1600没出来.那题确实有点离谱了..
真的一点思路没有
最近还打牛客多校..我感觉那天我们的状态太差了...我写题的时候要困死了.写的特别不清晰
幸好最好都出来了
最近也没学啥算法..感觉不想学,还是刷codeforces有趣啊..但是还得硬着头皮学啊!!
牛客多校第5场..有一个二分图的最大匹配问题的..
比赛时候看题意都看烦了..并且二分图也一点不会..所以也没做,我们2小时40分钟就下班了...
赛后发现过了快1000了,但是应该好多都是错误的..
这题需要用好多结论的..不补了..麻烦....
这题是背包dp+区间dp,然后我们也不会....
赛后发现有人用记忆化写出来了.但是有一个点我一直没搞懂
我们用mem[i][j][has]表示当前在第i个奶酪前(还没做任何处理,没挖掉也没拿走)第j个背包现在背包已经有has重量的最大重量
一共有3种情况:
1.如果当前奶酪可拿 :
if(z[v]-has>=a[u]){
res=b[u]+dfs(u+1,v,has+a[u]);
}
2.当前奶酪挖掉,到下一个奶酪并且不换背包
res=max(res,dfs(u+1,v,has));
3.在当前奶酪前换背包,更新当前的背包用了多少
res=max(res,dfs(u,v+1,0));
还有一个非常关键的点!!
我们发现每次背包都是递增的,如果m>=n考虑后n次即可(因为如果前面拿不了,后面可能能拿,后面拿不了前面一定拿不了),否则需要全部考虑
我困惑的点就是这个dp的第二维太离谱了..
如果m为20000,n为200的话,不就超了吗??
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
#include <list>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;//106110956
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int n,m;
int a[100005],b[100005],z[100005];
int dp[399][503][205];
int dfs(int u,int v,int has){
if(u>n||v>m){
return 0;
}
if(dp[u][v][has])return dp[u][v][has];
int res=0;
if(z[v]-has>=a[u]){
res=b[u]+dfs(u+1,v,has+a[u]);
}
res=max(res,dfs(u,v+1,0));
res=max(res,dfs(u+1,v,has));
dp[u][v][has]=res;
return dp[u][v][has];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
}
for(int i=1;i<=m;i++){
scanf("%d",&z[i]);
}
cout<<dfs(1,max(1,m-n+1),0)<<endl;
return 0;
}
这题有点意思.
次数要小于31,我次数优化到了34.
先说我想..感觉是误打误撞的..也没花多少时间,40分钟左右吧
我们统计最小值和最大值以及他们的位置
如果manx<=0的话,说明全为负数...
然后我就0...同时更新最小值
if(maxn<=0){
for(int i=n;i>=2;i--){
while(a[i]<a[i-1]){
a[i-1]=a[i-1]+minn;
++cnt;
ans[cnt].x=i-1;
ans[cnt].y=pos2;
}
if(a[i-1]<minn){
pos2=i-1;
minn=a[i-1];
}
}
后来我发现我写的太离谱了.
如果全为正数或者全为负数的话.直接相邻项相加即可(正负顺序不同)
如果maxn>0的话也是一样的..但是只判读这一种会超过50次
for(int i=2;i<=n;i++){
while(a[i]<a[i-1]){
a[i]=a[i]+maxn;
++cnt;
ans[cnt].x=i;
ans[cnt].y=pos1;
}
if(a[i]>maxn){
pos1=i;
maxn=a[i];
}
}
如果maxn>0且minn<0的话,我们取maxn和minn最小操作数即可
困难模式正解:
如果全部为正或者全为负,操作次数不超过19次
如果既有整数既有负数的话:我们可以数组全部变为正或者全部为负在排序
排序需要花19次,所以我们还有12次
我们可以构造一个大数,abs(x)>=32至多需要花费5次机会
设x1为造一个正的大数的次数,y1吧数列全部变为正数的次数
设x2为造一个负的大数的次数,y2把数列全部变为负数的次数
x1和x2之间一定有一个为0
因为绝对值最大的一定非正即负.
x1+x2<5
有y1+y2<=n<=20
所以x1+x2+y1+y2<=25
故min(x1+y1,x2+y2)<=12
我一直在考虑min(x1+y1,x2+y2)是否一定小于25/2向下取整
然后就举了几个例子..确实是对的
然后写了200行代码恶心
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
#include <list>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;//106110956
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int t;
int a[25];
struct Node{
int x,y;
}ans[105];
int b[25];
int main(){
scanf("%d",&t);
while(t--){
int x1,y1;
x1=0;//正数
y1=0;//负数
int n;
scanf("%d",&n);
int minn=inf;
int maxn=-inf;
int pos1;
int pos2;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>0){
x1++;
}
if(a[i]<0){
y1++;
}
b[i]=a[i];
if(a[i]>maxn){
maxn=a[i];
pos1=i;
}
if(a[i]<minn){
minn=a[i];
pos2=i;
}
}
int cnt=0;
if(maxn<=0){
for(int i=n-1;i>=1;i--){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i+1;
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
continue;
}
if(minn>=0){
for(int i=2;i<=n;i++){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i-1;
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
continue;
}
if(abs(maxn)>=abs(minn)){
if(y1+n-1<=31){
for(int i=1;i<=n;i++){
if(a[i]<0){
cnt++;
ans[cnt].x=i;
ans[cnt].y=pos1;
}
}
for(int i=2;i<=n;i++){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i-1;
}
}else{
for(int i=1;i<=5;i++){
cnt++;
ans[cnt].x=pos2;
ans[cnt].y=pos2;
}
for(int i=1;i<=n;i++){
if(a[i]>0){
cnt++;
ans[cnt].x=i;
ans[cnt].y=pos2;
}
}
for(int i=n-1;i>=1;i--){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i+1;
}
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
}else{
if(x1+n-1<=31){
for(int i=1;i<=n;i++){
if(a[i]>0){
cnt++;
ans[cnt].x=i;
ans[cnt].y=pos2;
}
}
for(int i=n-1;i>=1;i--){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i+1;
}
}else{
for(int i=1;i<=5;i++){
cnt++;
ans[cnt].x=pos1;
ans[cnt].y=pos1;
}
for(int i=1;i<=n;i++){
if(a[i]<0){
cnt++;
ans[cnt].x=i;
ans[cnt].y=pos1;
}
}
for(int i=2;i<=n;i++){
cnt++;
ans[cnt].x=i;
ans[cnt].y=i-1;
}
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
}
}
return 0;
}
今天唯一被难住的一道题
感觉挺难想的也挺有意思的
最后一直爆炸的怪兽对其他怪兽没有影响
之前爆炸的怪兽除了第一只之外将其打倒需要的子弹数为max(0,a[i+1]-b[i]);当前在i+1
我们只需要维护a[i+1]-max(0,a[i+1]-b[i])的最小值即可
然后最后在加上花费即可
好难想的!!!
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
#include <list>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;//106110956
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int t;
ll a[300005],b[300005];
int main(){
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i],&b[i]);
}
ll ans=0;
ll minn=1e18;
ll cnt=0;
for(int i=1;i<=n;i++){
int j;
if(i==n)j=1;
else j=i+1;
ans=ans+max(0ll,a[j]-b[i]);
if(a[j]-max(0ll,a[j]-b[i])<=minn){
minn=a[j]-max(0ll,a[j]-b[i]);
if(max(0ll,a[j]-b[i])==0){
cnt=a[j];
}else{
cnt=b[i];
}
}
}
ans=ans+cnt;
printf("%lld\n",ans);
}
return 0;
}