2016-2017 ACM-ICPC Northwestern European Regional Programming Contest (NWERC 2016)

A、给你一堆十进制数字,允许前导零,大小比较的方式同字典序
你需要更改最少的数位,使得整个序列单调不增

究极神秘的dp:
\(dp[i+1][j+k]=cal(dp[i][j])(0<=k<=2*n)\)

其中:
\(dp[i][j]\)表示前\(i\)个数字更改了\(j\)次之后所能得到的最小“数字”
这意味着dp数组里储存的是一个字符串

接下来是窒息的\(cal()\)函数,用于计算\(k\)步之内能得到的最小字符串,并且还要满足\(>=dp[i][j]\)

cal函数实际上贪心地找即可,先看一下能不能把每一位都设置得跟\(dp[i][j]\)一样
不能的话就向前找到第一位不是\(9\)的,然后把这儿\(+1\)即可

最后别忘了剩下的所有数位置\(0\),因为要保证最小

dp的初值弄一个全0的合法串即可,还要记录一下转移路径方便输出

代码:

#include <bits/stdc++.h>
#define FAST ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
using namespace std;
const int N=45;

int n,m;
string s[N];
string dp[N][N*2];
int pre[N][N*2];
bool can[N][N*2];

bool gao(string &p,string s,string t,int k)
{
    p=s;
    int cnt=k,i=0;
    for(;i<m&&cnt>0;++i)
    {
        if(p[i]!=t[i])
        {
            p[i]=t[i];
            cnt--;
        }
    }
    if(p>=t) return 1;
    for(--i;i>=0&&p[i]=='9';--i);//
    if(i<0) return 0;
    p=s;
    cnt=k;
    for(int j=0;j<i;++j)
    {
        if(p[j]!=t[j])
        {
            p[j]=t[j];
            cnt--;
        }
    }
    if(p[i]!=t[i]+1)
    {
        p[i]=t[i]+1;
        cnt--;
    }
    for(int j=i+1;j<m&&cnt>0;++j)
    {
        p[j]='0';
        cnt--;
    }
    return 1;
}

void gao2()
{
    can[0][0]=1;
    for(int i=0;i<m;++i)
        dp[0][0]+='0';
}

int main()
{
    FAST
    cin>>n>>m;for(int i=1;i<=n;++i) cin>>s[i];
    gao2();
    for(int i=0;i+1<=n;++i)
    {
        for(int j=0;j<=2*n;++j)
        {
            if(!can[i][j]) continue;
            string p=s[i+1];
            for(int k=0;j+k<=2*n;++k)
            {
                if(gao(p,s[i+1],dp[i][j],k)&&(!can[i+1][j+k]||dp[i+1][j+k]>p))
                {
                    can[i+1][j+k]=1;
                    pre[i+1][j+k]=j;
                    dp[i+1][j+k]=p;
                }
            }
        }
    }
    stack<string> ans;
    for(int j=0;j<=2*n;++j)
    {
        if(can[n][j])
        {
            for(int i=n;i>=1;--i)
            {
                ans.push(dp[i][j]);
                j=pre[i][j];
            }
            break;
        }
    }
    while(!ans.empty())
    {
        cout<<ans.top()<<endl;
        ans.pop();
    }
    return 0;
}

B、有向图最长链,本来是NP问题,但是有限定环的大小不超过5

那么就先缩点,然后SCC内暴力转移,再把dp值转移给相邻的SCC,拓扑排序即可

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=0x3f3f3f3f;

int n,m,x[N*10],y[N*10],tmp[N*10];
vector<int> e[N],scc[N],e2[N];
int dfn[N],low[N],timer=0,c[N],color=0;
int deg[N],dp[N];
bool vis[N];
stack<int> s;

void add(int x,int y){e[x].push_back(y);}
void add2(int x,int y){e2[x].push_back(y);}

void tarjan(int u)
{
    dfn[u]=low[u]=++timer;
    s.push(u);
    vis[u]=1;
    for(auto v:e[u])
    {
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ++color;
        int t;
        do
        {
            t=s.top();
            s.pop();
            c[t]=color;
            vis[t]=0;
            scc[color].push_back(t);
        }
        while(u!=t);
    }
}

void gao2(int x,int cnt)
{
    vis[x]=1;
    for(auto y:e[x])
    {
        if(vis[y]==0&&c[x]==c[y])//
            gao2(y,cnt+1),dp[y]=max(dp[y],cnt+1);
        else if(c[x]!=c[y])//
            dp[y]=max(dp[y],1+cnt);
    }
    vis[x]=0;
}

void gao()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;++i)
    {
        if(c[x[i]]!=c[y[i]])
            add2(c[x[i]],c[y[i]]),deg[c[y[i]]]++;
    }
    queue<int> q;
    for(int i=1;i<=color;++i)
        if(deg[i]==0)
            q.push(i);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(auto i:scc[x])
            tmp[i]=dp[i];
        for(auto i:scc[x])
            gao2(i,tmp[i]);
        for(auto y:e2[x])
        {
            deg[y]--;
            if(deg[y]==0)
                q.push(y);
        }
    }
//  for(int i=1;i<=n;++i) printf("? %d ",dp[i]);
    int ans=-inf;
    for(int i=1;i<=n;++i) ans=max(ans,dp[i]);
    printf("%d",ans+1);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&x[i],&y[i]);
        add(x[i],y[i]);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i])
            tarjan(i);
    gao();
    return 0;
}

C、签到,垂直方向和水平方向速度不变,在某些区域水平方向会暂时加速,问你著速度是多少才能恰好抵达某个点

实际上就是解一元一次方程

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
double x,y,f;
int n,l,r;
 
int main()
{
    scanf("%lf%lf",&x,&y);
    scanf("%d",&n);
    double sum=0.0,tmp=0.0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d%d%lf",&l,&r,&f);
        sum+=(r-l)*f;
        tmp+=r-l;
    }
    sum+=y-tmp;
    printf("%.10lf",x/sum);
    return 0;
}

E、签到,题意比较迷,老师会选一个房间开始发卷子,每个房间会发出这个房间里的人数张卷子,然后再收进来人数张卷子,问你一圈下来会不会有人收到自己的卷子

从人数最多的房间开始发,模拟一遍判断第一个房间的人有没有拿到自己卷子即可

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35;
const int inf=0x3f3f3f3f;
 
int n;
pair<int,int> p[N];
vector<int> ans;
queue<int> q;
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&p[i].first);
        p[i].second=i;
    }
    sort(p+1,p+1+n);
    ans.push_back(p[n].second);
    for(int i=1;i<=p[n].first;++i)
        q.push(p[n].second);
    for(int i=n-1;i>=1;--i)
    {
        for(int j=1;j<=p[i].first;++j)
            q.pop();
        for(int j=1;j<=p[i].first;++j)
            q.push(p[i].second);
        ans.push_back(p[i].second);
    }
    bool can=1;
    int f;
    while(!q.empty())
    {
        f=q.front();
        if(f==p[n].second)
        {
            can=0;
            break;
        }
        q.pop();
    }
    if(can)
    {
        for(auto i:ans) printf("%d ",i);
    }
    else
    puts("impossible");
    return 0;
}

F、队友写的二分

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n,a[1000005],b[1000005];
 
bool check(int x)
{
    vector<int> A,B;
    for(int i=0;i<n;i++)
    {
        if(a[i]>x) A.push_back(a[i]);
        if(b[i]>x) B.push_back(b[i]);
    }
    if(A.size()%2||B.size()%2) return false;
    for(int i=0;i<A.size();i+=2)
        if(A[i]!=A[i+1]) return false;  
    for(int i=0;i<B.size();i+=2)
        if(B[i]!=B[i+1]) return false;
    return true;
}
 
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    for(int i=0;i<n;i++)
        scanf("%d",b+i);
    int l=0,r=1000000000;
    while(l<r)
    {
        int m=l+r>>1;
        if(check(m)) r=m; else l=m+1;   
    }
    printf("%d",l);
    return 0;
}

H、队友写的格雷码

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n;
ll a[100],b[100],A,B;
 
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%1lld",&a[i]);
    for(int i=0;i<n;i++)
        scanf("%1lld",&b[i]);
    for(int i=1;i<n;i++)
        a[i]^=a[i-1],b[i]^=b[i-1];
    for(int i=0;i<n;i++)
    {
        A<<=1,B<<=1;
        A+=a[i],B+=b[i];
    }
    printf("%lld",max(0ll,abs(A-B)-1));
    return 0;
}

I、怪怪最短路,有两种矿井,每个点都可能是其中的一种,求从1开始到A矿井和B矿井经过的点数最少是多少

考虑跑三次dij,分别从1、A的超级源点、B的超级源点三个位置开始,求出1到所有点、所有A到所有点、所有B到所有点的最短路

注意A和B的超级源点要跑反向边,因为是有向图

然后枚举每个点即可,答案是三个距离之和的最小值

正确性有个感性的证明,答案点应该是一个“三岔口”,它经过的重复点数最少

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
 
int n,m,C,O,diso[100005],disc[100005],dis[100005],vis[100005],o[100005],c[100005];
struct edge
{
    int y,v;
    edge(int Y,int V):y(Y),v(V){}
};
vector<edge> e[100005],rev[100005];
 
inline void add(int x,int y,int v)
{
    e[x].emplace_back(y,v);
}
 
inline void revadd(int x,int y,int v)
{
    rev[x].emplace_back(y,v);
}
 
void dij(int dis[],vector<edge> e[],int s)
{
    //memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    priority_queue<pii,vector<pii>,greater<pii>> q;
    q.emplace(0,s);
    while(!q.empty())
    {
        int x=q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        for(auto y:e[x])
            if(dis[x]+y.v<dis[y.y])
            {
                dis[y.y]=dis[x]+y.v;
                q.emplace(dis[y.y],y.y);
            }
    }
}
 
int main()
{
    scanf("%d%d%d",&n,&O,&C);
    for(int i=0,x;i<O;i++)
    {
        scanf("%d",&x);
        revadd(n+1,x,0);
    }
    for(int i=0,x;i<C;i++)
    {
        scanf("%d",&x);
        revadd(n+2,x,0);
    }
    for(int i=1,tot,x;i<=n;i++)
    {
        scanf("%d",&tot);
        for(int j=0;j<tot;j++)
            scanf("%d",&x),add(i,x,1),revadd(x,i,1);
    }
    memset(dis,0x3f,sizeof(dis));
    memset(disc,0x3f,sizeof(disc));
    memset(diso,0x3f,sizeof(diso));
    dij(diso,rev,n+1);
    dij(disc,rev,n+2);
    dij(dis,e,1);
    ll ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
        ans=min(ans,(long long)diso[i]+disc[i]+dis[i]);
    if(ans>=0x3f3f3f3f) printf("impossible"); else printf("%lld",ans);
    return 0;
}

J、队友说似乎是个大模拟?欧洲人的英语确实有点迷惑

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main()
{
    int n,q,s;
    ll sumd=0;
    ll suma=0;
    int tar[105]={0};
    ll qsize[35]={0};
    ll qleft[35]={0};
    ll buffer=0;
    
    scanf("%d %d %d",&n,&q,&s);
    for(int i=1;i<=s;i++)
    {
        scanf("%d",tar+i);
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%lld",qsize+i);
        qleft[i]=qsize[i];
    }
    ll par,tmp;
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&tmp);
        sumd+=tmp;
        ll qthis[35]={0};
        for(int j=1;j<=s;j++)
        {
            scanf("%lld",&par);
            qthis[tar[j]]+=par;
            suma+=par;
        }
        for(int j=1;j<=q;j++)
        {
            if(qthis[j]>qsize[j])
            {
                cout<<"impossible";
                return 0;
            }
        }
        for(int j=1;j<=q;j++)
        {
            qleft[j]-=qthis[j];
            if(qleft[j]<0)
            {
                buffer+=qleft[j];
                qleft[j]=0;
                if(buffer<0)
                {
                    cout<<"impossible";
                    return 0;
                }
            }
        }
        buffer+=tmp;
    }
    if(sumd>=suma)
    {
        cout<<"possible";
    }
    else
    {
        cout<<"impossible";
    }
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/oneman233/p/11771386.html