NOIP2017提高组 模拟赛23(总结)
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位。
②
【代码】
#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;
}