欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NOIP2017提高组 模拟赛23(总结)

程序员文章站 2022-03-14 19:19:50
...

NOIP2017提高组 模拟赛23(总结)

第一题 Hacker

【题目描述】

【解题思路】

  只需要两段假期,而且天数也不会超过10^6。直接用数组记下就好。排个序,遇头统计前面的是否有符合的,遇尾就把这段假期加进数组里。

【代码】

#include<cstdio>
#include<algorithm>

#define imin(a,b) ((a<b)?(a):(b))

using namespace std;

const int N=1000100;
const int oo=1000000100;
int n,m,n2,ans;
int L[N],R[N],X[N],mm[N];
struct data { int x,id; bool fl; } d[N<<1];

bool cmp(data A,data B) { return (A.x<B.x || (A.x==B.x && (A.fl==0 && B.fl==1))); }

int main()
{
    freopen("1982.in","r",stdin);
    freopen("1982.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=0;i<=m;i++) mm[i]=oo;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&L[i],&R[i],&X[i]);
        d[i].id=i; d[i].x=L[i]; d[i].fl=0;
        d[i+n].id=i; d[i+n].x=R[i]; d[i+n].fl=1;
    }
    n2=n<<1;
    sort(d+1,d+1+n2,cmp);
    ans=oo<<1;
    for(int i=1;i<=n2;i++)
    {
        int id=d[i].id;
        int len=R[id]-L[id]+1;
        if(d[i].fl==1) mm[len]=imin(mm[len],X[id]); else
        {
            if(len>=m) continue;
            if(mm[m-len]!=oo) ans=imin(ans,mm[m-len]+X[id]);
        }
    }
    printf("%d\n",(ans==(oo<<1))?(-1):(ans));
    return 0;
}

第二题 记字符串 (状压DP)

【题目描述】

【解题思路】

  字符串的长度和个数都不超过20。
  假如,要用第i位来使第i位相同的字符串独立,有两种情况:
  ①将所有相同的字符改成其他字符,最大的不用改。
  代价=所有第i位相同的字符串改第i位的代价的总和-字符串改第i位的最大代价。
  ②还有一种就是改单个字符。
  代价=最小的改一位的代价(从1~m位中改最小代价的那一位)。
  因为只有20个字符串,一定可以改成与其他字符不同的字符。
  状态压缩DP。
  记现在处理到第w位,已经独立的字符串的状态为S的最小代价。
  预处理出将第i个字符串的第j位改掉(用① 的改法),所影响的字符串集合Wij.S,代价为Wij.cost。
  还有第②中改法的单个字符串i最小代价mn[i]。
  找出S的未被独立的最小的i。
  ①枚举改第j位。F[S|Wij.S]=min(F[S|Wij.S],F[S]+Wij.cost)
  ②F[S|(1<<(i1))]=min(F[S|(1<<(i1))],F[S]+mn[i])

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>

#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))

#define fn (1<<n)

using namespace std;

const int inf=1e9;
const int N=25;
const int big=1500000;
int n,m;
struct data { int stu,cs; } w[N][N];
int cs[N][N],d[N][N],f[big],mn[N];
char st[N];

int main()
{
    freopen("1983.in","r",stdin);
    freopen("1983.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",st);
        for(int j=0;j<m;j++) d[i][j+1]=st[j]-'a'+1;
    }
    for(int i=1;i<=n;i++)
    {
        mn[i]=inf;
        for(int j=1;j<=m;j++) scanf("%d",&cs[i][j]),mn[i]=imin(mn[i],cs[i][j]);
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int ysum=0,mx=0;
            int stu=0;
            for(int h=1;h<=n;h++)
            if(d[h][j]==d[i][j])
            {
                stu+=(1<<(h-1));
                ysum+=cs[h][j];
                mx=imax(mx,cs[h][j]);
            }
            w[i][j].stu=stu;
            w[i][j].cs=ysum-mx;
        }
    }

    for(int i=1;i<fn;i++) f[i]=inf;
    f[0]=0;
    for(int i=0;i<fn;i++)
    if(f[i]!=inf)
    {
        int last=0;
        while(i&(1<<last)) last++;
        if(last>=n) continue;
        int yu;
        for(int j=1;j<=m;j++)
        {
            yu=i|(w[last+1][j].stu);
            f[yu]=imin(f[yu],f[i]+w[last+1][j].cs);
        }
        yu=i|(1<<(last));
        f[yu]=imin(f[yu],f[i]+mn[last+1]);
    }
    printf("%d\n",f[fn-1]);
    return 0;
}

第三题 士兵与旅行 (网络流)

【题目描述】

【解题思路】

  城市和道路构成一幅图,每一个城市的士兵只能去相邻的城市。一开始的状态为S1,结束状态为S2。
  先在需要从S1转到S2。
  可以对每一个城市建立两个点Ai,Bi,分别表示转移之前的和转移之后的。
  若城市i可以到达城市j,则Ai->Bj,流量无穷大。(因为士兵可以从i转移到j,流了多少就表示转移多少)
  从源点S->城市i,流量为一开始城市i的士兵数。
  城市i->汇点T,流量为转移之后的士兵数。
  判断流出流量是否等于流入流量就好。
  若等于,则一定存在转移方案。城市Ai->城市Bj的反向边的流量就是转移方案(因为记录的是剩余流量,开始为∞,流了w,变成∞-w。那么反向边的流量就是w)。

【代码】

#include<cstdio>
#include<algorithm>

#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))
#define ad(i) (i)
#define bd(i) (n+i)

using namespace std;

const int inf=1e9;
const int N=120;
const int M=220;
const int maxm=1000000;
int n,m,sum_a,sum_b;
int A[N],B[N];
int bfstime,q[maxm],lev[maxm],vis[maxm];
int h[maxm],d0,S,T;
int yu[N][N];
struct data { int val,to,ne; } d[maxm];

void addedge(int a,int b,int c)
{
    d0++;
    d[d0].to=b;d[d0].val=c;d[d0].ne=h[a];h[a]=d0;
    d0++;
    d[d0].to=a;d[d0].val=0;d[d0].ne=h[b];h[b]=d0;
}

bool bfs()
{
    int head=1,tail=1;
    q[head]=S;
    vis[S]=++bfstime;
    lev[S]=1;
    while(head<=tail)
    {
        for(int p=h[q[head]];p;p=d[p].ne)
        {
            if(!d[p].val) continue;
            int to=d[p].to;
            if(vis[to]==bfstime) continue;
            q[++tail]=to;
            vis[to]=bfstime;
            lev[to]=lev[q[head]]+1;
        }
        head++;
    }
    return vis[T]==bfstime;
}

int dfs(int now,int maxf)
{
    int ret=0,t;
    if(now==T || !maxf ) return maxf;
    for(int p=h[now];p;p=d[p].ne)
    {
        int to=d[p].to;
        if(!d[p].val || lev[to]!=lev[now]+1) continue;
        t=dfs(to,imin(maxf,d[p].val));
        d[p].val-=t;
        d[p^1].val+=t;
        maxf-=t;
        ret+=t;
    }
    if(maxf) lev[now]=-1;
    return ret;
}

int dinic()
{
    int ret=0;
    while(bfs()) ret+=dfs(S,1e9);
    return ret;
}

int main()
{
    freopen("1984.in","r",stdin);
    freopen("1984.out","w",stdout);
    scanf("%d%d",&n,&m); d0=1;
    S=(n<<1)+1; T=S+1;
    sum_a=sum_b=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&A[i]);
        addedge(S,ad(i),A[i]);
        sum_a+=A[i];
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&B[i]);
        addedge(bd(i),T,B[i]);
        addedge(ad(i),bd(i),inf);
        sum_b+=B[i];
    }
    for(int i=1;i<=m;i++)
    {
        int a,b; scanf("%d%d",&a,&b);
        addedge(ad(a),bd(b),inf);
        addedge(ad(b),bd(a),inf);
    }
    int sum_la=dinic();
    if(sum_la==sum_b)
    {
        printf("YES\n");
        for(int i=1;i<=n;i++)
        {
            int u=bd(i);
            for(int p=h[u];p;p=d[p].ne)
            {
                if(!(p&1)) continue;
                int to=d[p].to;
                yu[to][i]+=d[p].val;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++) printf("%d ",yu[i][j]);
            printf("\n");
        }
    } else printf("NO\n");
    return 0;
}