排个序,如果相邻的数大于一就不满足题意
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=110;
int a[N];
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
bool ok=1;
for(int i=0;i<n-1;i++)
if(a[i+1]>a[i]+1)
{
ok=0;
break;
}
if(ok) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
礼物最终个数肯定是最小的那个。每个礼物减小到最少的那个,注意可以同时减少两种礼物。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=110;
ll a[N],b[N];
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
ll mina=2e9;
ll minb=2e9;
for(int i=0;i<n;i++)
{
cin>>a[i];
mina=min(mina,a[i]);
}
for(int i=0;i<n;i++)
{
cin>>b[i];
minb=min(minb,b[i]);
}
ll res=0;
for(int i=0;i<n;i++) res+=max(a[i]-mina,b[i]-minb);
cout<<res<<endl;
}
return 0;
}
枚举+双指针
由于体重非常小,两两配对的体重和也很小,直接暴力枚举两两体重和w。现在问题转化成原序列找出两个数的和等于w,双指针。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=110;
int a[N],n;
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
if(n==1)
{
cout<<0<<endl;
continue;
}
sort(a+1,a+n+1);
int res=0,mx=0;
for(int w=a[1]+a[2];w<=a[n-1]+a[n];w++)
{
int cnt=0;
for(int i=1,j=n;i<j;i++)
{
while(j>i&&a[i]+a[j]>w) j--;
if(j<=i) break; //注意这点 因为这wa了一次
if(a[i]+a[j]==w) cnt++,j--;
}
res=max(res,cnt);
}
cout<<res<<endl;
}
return 0;
}
贪心+二分
贪心:如果该位是1那么找一个末尾是0的序列放进去如果不存在那么新添一组
比如目前有五组0 0 1 1 1
分别表示该组最后一个字符是0或者1,如果该位是1我们就二分出最后一个0,pos=2,组别表示变成0 1 1 1 1
。如果该位使0我们二分处第一个1的位置,pos=3,组别表示变成0 0 0 1 1
,这样操作可以发现组别表示的序列始终是有序的,保证时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
好像有 O ( n ) O(n) O(n)的做法奈何wctl,写题的时候只想到这个做法。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=200010;
int pos[N],a[N];
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
int n;
string s;
cin>>n>>s;
int cnt=1;
a[1]=s[0]-'0';
pos[0]=1;
for(int i=1;i<n;i++)
{
int t=s[i]-'0';
if(t)
{
int l=1,r=cnt;
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<=0) l=mid;
else r=mid-1;
}
if(a[l]==0)
{
a[l]=1;
pos[i]=l;
}
else
{
a[++cnt]=1;
pos[i]=cnt;
}
}
else
{
int l=1,r=cnt;
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=1) r=mid;
else l=mid+1;
}
if(a[l]==1)
{
a[l]=0;
pos[i]=l;
}
else
{
a[++cnt]=0;
pos[i]=cnt;
}
}
}
cout<<cnt<<endl;
for(int i=0;i<n;i++) cout<<pos[i]<<" ";
cout<<endl;
}
return 0;
}
这次做了4个题,差一点做出了E1。
我没想到这次我不在意的点就是这题的关键的!!!
先dfs一下把从根节点到每个叶子节点每条边跑的次数记录一下,记作cnt[i]
,如果该边边权为w[i]
如果把这条边砍一半,那么总的路程减小(w[i]-w[i]/2)*cnt[i]
,显然贪心,用个优先队列(按照减小的多优先级高)维护一下就行了。
坑点:很容易想到按照w[i]*cnt[i]
排序,不过这题下取整导致问题不断。(我做的时候想这题应该不会那么麻烦,就图个省事结果。。。)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx;
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
ll w;
int num;
bool operator <(const node&o)const
{
ll sub1=(w-w/2)*num;
ll sub2=(o.w-o.w/2)*o.num;
return sub1<sub2;
//其实也可以直接 return (w+1)/2*num<(o.w+1)/2*o.num; 上去整
}
};
priority_queue<node> q;
void add(int a,int b,ll c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
d[b]++;//统计度数,度数为1是叶子节点
}
int dfs(int u,int fa)
{
int lcnt=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
cnt[i]+=(d[j]==1);
cnt[i]+=dfs(j,u);
lcnt+=cnt[i];
}
return lcnt;
}
void init()
{
for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
idx=res=0;
while(q.size()) q.pop();
}
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
cin>>n>>limit;
init();
for(int i=1;i<n;i++)
{
int a,b;
ll c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs(1,-1);
for(int i=0;i<2*n-2;i+=2)
{
q.push({w[i],cnt[i]+cnt[i+1]});
res+=w[i]*(cnt[i]+cnt[i+1]);
}
ll ans=0;
while(res>limit)
{
auto t=q.top();
q.pop();
q.push({t.w/2,t.num});
res-=1ll*(t.w-t.w/2)*t.num;
ans++;
}
cout<<ans<<endl;
}
return 0;
}
E1砍边都是花费1代价,而E2就是有些边砍一半需要花费1代价有些2代价。显然我们考虑用两个优先队列维护q1,q2
,q1
维护花费1代价的 q2
维护花费2代价的。仍然贪心。每次考虑花费1代价砍边,如果发现花两次1代价减小的路程小于直接花费2代价砍边减小的路程,那么就直接花费2代价砍边。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100010,M=200010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
ll w;
int num;
bool operator <(const node&o)const
{
ll sub1=(w-w/2)*num;
ll sub2=(o.w-o.w/2)*o.num;
return sub1<sub2;
}
};
priority_queue<node> q1,q2;
void add(int a,int b,ll c,int coin)
{
e[idx]=b;
w[idx]=c;
cost[idx]=coin;
ne[idx]=h[a];
h[a]=idx++;
d[b]++;
}
int dfs(int u,int fa)
{
int lcnt=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
cnt[i]+=(d[j]==1);
cnt[i]+=dfs(j,u);
lcnt+=cnt[i];
}
return lcnt;
}
void init()
{
for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
idx=res=0;
while(q1.size()) q1.pop();
while(q2.size()) q2.pop();
q1.push({0,0}),q1.push({0,0}),q2.push({0,0});//哨兵,避免很多边界情况。
}
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
cin>>n>>limit;
init();
for(int i=1;i<n;i++)
{
int a,b,coin;
ll c;
cin>>a>>b>>c>>coin;
add(a,b,c,coin);
add(b,a,c,coin);
}
dfs(1,-1);
for(int i=0;i<2*n-2;i+=2)
{
if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});
else q2.push({w[i],cnt[i]+cnt[i+1]});
res+=w[i]*(cnt[i]+cnt[i+1]);
}
ll ans=0;
while(res>limit)
{
auto a=q1.top();q1.pop();
auto b=q1.top();q1.pop();
auto c=q2.top();q2.pop();
ll suba=(a.w-a.w/2)*a.num;
ll subb=max((a.w/2-a.w/4)*a.num,(b.w-b.w/2)*b.num);
ll subc=(c.w-c.w/2)*c.num;
if(res-suba<=limit)
{
ans++;
break;
}
else
{
if(suba+subb<=subc)
{
res-=subc;
q1.push(a),q1.push(b);
q2.push({c.w/2,c.num});
ans+=2;
}
else
{
res-=suba;
q1.push(b),q1.push({a.w/2,a.num});
q2.push(c);
ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
注意:每次只考虑花费1代价砍边
我在写的时候写了一份每次考虑花费2代价砍边。比较两次1代价和一次2代价减小的路程吗,这样贪心其实不正确,非常dt。
下面代码是错误的贪心思路每次考虑花费2代价砍边
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
ll w;
int num;
bool operator <(const node&o)const
{
ll sub1=(w-w/2)*num;
ll sub2=(o.w-o.w/2)*o.num;
return sub1<sub2;
}
};
priority_queue<node> q1,q2;
void add(int a,int b,ll c,int coin)
{
e[idx]=b;
w[idx]=c;
cost[idx]=coin;
ne[idx]=h[a];
h[a]=idx++;
d[b]++;
}
int dfs(int u,int fa)
{
int lcnt=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
cnt[i]+=(d[j]==1);
cnt[i]+=dfs(j,u);
lcnt+=cnt[i];
}
return lcnt;
}
void init()
{
for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
idx=res=0;
while(q1.size()) q1.pop();
while(q2.size()) q2.pop();
}
int main()
{
IO;
int T;
cin>>T;
while(T--)
{
cin>>n>>limit;
init();
for(int i=1;i<n;i++)
{
int a,b,coin;
ll c;
cin>>a>>b>>c>>coin;
add(a,b,c,coin);
add(b,a,c,coin);
}
dfs(1,-1);
for(int i=0;i<2*n-2;i+=2)
{
if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});
else q2.push({w[i],cnt[i]+cnt[i+1]});
res+=w[i]*(cnt[i]+cnt[i+1]);
}
q1.push({0,0}),q1.push({0,0}),q2.push({0,0});
ll ans=0;
while(res>limit)
{
auto a=q1.top();q1.pop();
auto b=q1.top();q1.pop();
auto c=q2.top();q2.pop();
ll suba1=(a.w-a.w/2)*a.num;
ll suba2=(a.w/2-a.w/4)*a.num;
ll subb=(b.w-b.w/2)*b.num;
ll subc=(c.w-c.w/2)*c.num;
if(res-suba1<=limit)
{
ans++;
break;
}
else if(res-suba1-max(suba2,subb)<=limit||res-subc<=limit)
{
ans+=2;
break;
}
else if(res-suba1-subc<=limit)
{
ans+=3;
break;
}
else
{
if(suba2>subb)
{
if(suba1+suba2>subc)
{
res-=suba1+suba2;
q1.push(b),q1.push({a.w/4,a.num});
q2.push(c);
}
else
{
res-=subc;
q1.push(a),q1.push(b);
q2.push({c.w/2,c.num});
}
}
else
{
if(suba1+subb>subc)
{
res-=suba1+subb;
q1.push({b.w/2,b.num}),q1.push({a.w/2,a.num});
q2.push(c);
}
else
{
res-=subc;
q1.push(a),q1.push(b);
q2.push({c.w/2,c.num});
}
}
ans+=2;
}
}
cout<<ans<<endl;
}
return 0;
}
嘻嘻还是div3能上一点分QvQ,希望今天div2掉少点分(div2日常掉分)-。-,要加油哦~