abc277

D - Takahashi's Solitaire

思路:

啥也不说了,一道让我气到吐血的题,记录一下精神不太好的发病期。

代码:

#include <bits/stdc++.h>
#define ll long long
const int maxn = 2e5 + 10;
using namespace std;
int n;
ll m;
ll a[maxn],b[maxn];
ll res,ans;
map<ll,ll>s;
int main()
{
  cin >> n >> m;
  int t=0;
  for (int i = 1; i <= n; i++)
  {
    cin >> b[i];
    res += b[i];//总和
  }
  sort(b+ 1, b + 1 + n);

  ll sum=b[n];//记录最大值
  ans=b[n];//记录连续和
  //从后往前遍历,s[i]记录数i目前能减去的数和
  for(int i=n-1;i>=1;i--)
  {
    if(b[i]==b[i+1])
    {
      ans+=b[i];
      s[b[i]]=ans;
      sum=max(sum,ans);
    }
    else if((b[i]+1)%m==b[i+1])
    {
      s[b[i+1]]=ans;
      ans+=b[i];
      sum=max(sum,ans);
    }
    else 
    {
      s[b[i+1]]=ans;
      sum=max(sum,ans);
      ans=b[i];
    }
  }
  s[b[1]]=ans;//记得更新第一个数
  //更新s[i],(i+1)%m的范围在[0,m)的情况
  for(int i=n;i>=1;i--)
  {
    if(b[i]>=(m)&&b[i+1]!=b[i])
    {
      if(s[(b[i]+1)%m])
      {
        s[b[i]]+=s[(b[i]+1)%m];
        sum=max(sum,s[b[i]]);
      }
    }
  }
  //需要特判若出现m-1的数取模时是否要加上s[0]
  //记录与0连续的区间,与m-1连续的区间
  //若区间不重合,则加上
  int l0=-1,r0=-1,lm=-1,rm=-1;
  if(b[0]==0)l0=1;
  for(int i=1;i<=n-1;i++)if(b[1]==0&&(b[i]+1<b[i+1])){r0=i;break;}
  for(int i=n;i>=1;i--)
  {
    if(b[i]==m-1)rm=max(rm,i);
    else if(rm!=-1&&(b[i]+1<b[i+1])){lm=i+1;break;}
  }
  //区间不重合,把与m-1的区间最左边的数加上s[0]
  if(l0!=-1&&lm!=-1&&r0<lm)
  {
    s[b[lm]]+=s[0];
    sum=max(sum,s[b[lm]]);
  }
  for(auto x:s)
  {
    // cout<<x.first<<":"<<x.second<<endl;
    sum=max(sum,x.second);
  }
  //cout<<"res:"<<res<<" ans:"<<ans<<endl;
  cout << res - sum << endl;
  system("pause");
  return 0;
}

E - Crystal Switches

思路:很妙的方法,通过将有按钮的节点连接它的两个01图。

参考:mibamiba_

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;
struct node
{
  int to,d;//到达点以及权值
};
//优先队列排序
struct cmp1
{
  bool operator()(node u,node v)
  {
    return u.d>v.d;
  }
};
priority_queue<node,vector<node>,cmp1>q;
bool vh[maxn];//记录某点是否走过
vector<node>w[maxn];
int n,m,k,u,v,x,dis[maxn];
void dijkstra()
{
  q.push(node{1,0});
  memset(dis,127,sizeof(dis));//初始化设置为正无穷
  dis[1]=0;
  while(!q.empty())
  {
    node p=q.top();//队头
    q.pop();//出队
    if(vh[p.to])continue;//判断该点是否经过
    vh[p.to]=true;
    for(int i=0;i<w[p.to].size();i++)//遍历通过该点到达的所有点
    {
      int ls=w[p.to][i].to,ns=w[p.to][i].d;//到达的节点以及权值
      if(dis[ls]>dis[p.to]+ns)//更新最短路
      {
        dis[ls]=dis[p.to]+ns;
        q.push(node{ls,dis[ls]});//将新节点以及最短路放进去
      }
    }
  }
  if(min(dis[n],dis[n+n])>1e9)cout<<"-1"<<endl;
  else cout<<min(dis[n],dis[n+n])<<endl;
}
int main()
{
  cin>>n>>m>>k;
  for(int i=1;i<=m;i++)
  {
    cin>>u>>v>>x;
    //w[u]表示1图,w[u+n]表示0图
    if(x)w[u].push_back(node{v,1}),w[v].push_back(node{u,1});
    else w[u+n].push_back(node{v+n,1}),w[v+n].push_back(node{u+n,1});
  }
  for(int i=1;i<=k;i++)
  {
    cin>>x;
    //有按钮的节点可通过连接的所有点,连接x的0,1图
    w[x].push_back(node{x+n,0});
    w[x+n].push_back(node{x,0});
  }
  dijkstra();
  //system("pause");
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值