NOIP2010題解
阿新 • • 發佈:2018-11-06
NOIP2010題解
顯然原來都寫過,都重新寫一遍。
機器翻譯 translate
一道很容易的模擬題,直接使用一個佇列維護一下順序就好了。
#include<iostream> #include<cstdio> #include<queue> using namespace std; inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } bool vis[1050]; int Q[1050],h,t,ans,n,m; int main() { m=read();n=read();h=1;t=0; for(int i=1;i<=n;++i) { int x=read();if(vis[x])continue; Q[++t]=x;++ans;vis[x]=true; if(t-h+1>m)vis[Q[h++]]=false; } printf("%d\n",ans); return 0; }
烏龜棋 tortoise
一個不難想的\(dp\)是設\(f[i][a1][a2][a3][a4]\)表示當前在\(i\)位置,四種卡牌分別用的張數為\(a1,a2,a3,a4\)時的最大收益。不難發現\(i\)可以通過後面四個數算出來,顯然是一個多餘狀態,去掉後直接\(dp\)即可。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define MAX 400 void cmax(int &x,int y){if(x<y)x=y;} inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m; int a[MAX],b[5]; int f[45][45][45][45]; int main() { n=read();m=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=m;++i)b[read()]+=1; memset(f,-63,sizeof(f)); f[0][0][0][0]=a[1]; for(int i=0;i<=b[1];++i) for(int j=0;j<=b[2];++j) for(int k=0;k<=b[3];++k) for(int l=0;l<=b[4];++l) { int s=1*i+2*j+3*k+4*l+1; if(i)cmax(f[i][j][k][l],f[i-1][j][k][l]+a[s]); if(j)cmax(f[i][j][k][l],f[i][j-1][k][l]+a[s]); if(k)cmax(f[i][j][k][l],f[i][j][k-1][l]+a[s]); if(l)cmax(f[i][j][k][l],f[i][j][k][l-1]+a[s]); } printf("%d\n",f[b[1]][b[2]][b[3]][b[4]]); return 0; }
關押罪犯 prison
很妙的題目,如果沒有做過類似題目的話自己基本不可能做出來。
不難想到的一點是這樣的,我們把所有限制按照權值從大往小排序,因為要最大值最小,所以顯然竟可能的滿足權值較大的限制。但是我們發現限制是\(x,y\)不能在一個集合。那麼對於每一個點拆成兩個,一個表示和它在一個集合,一個表示和它不在一個集合。對於一個限制,如果\(x,y\)已經在一個集合內,顯然這個限制的權值就是答案了。否則合併\(x\)和\(y\)的集合關係,即把\(x\)合併到和\(y\)不在一個集合內的集合中去,\(y\)同理。並查集實現即可。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define MAX 40040 #define MAXL 100100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m; struct Line{int u,v,w;}e[MAXL]; bool operator<(Line a,Line b){return a.w>b.w;} int f[MAX]; int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);} int main() { n=read();m=read(); for(int i=1;i<=m;++i)e[i].u=read(),e[i].v=read(),e[i].w=read(); sort(&e[1],&e[m+1]); for(int i=1;i<=n+n;++i)f[i]=i; for(int i=1;i<=m;++i) { int u=e[i].u,v=e[i].v; if(getf(u)==getf(v)) { printf("%d\n",e[i].w); return 0; } f[getf(u)]=getf(v+n); f[getf(v)]=getf(u+n); } printf("0\n"); return 0; }
引水入城 flow
也是一道很不錯的題目。
對於判定性問題,不用多想,在每一個位置都建立蓄水場,\(bfs\)一遍判定是否能夠滿足所有位置。如果不行就直接輸出。否則考慮如何計算最小值。
繼續觀察,發現當可以滿足所有位置的時候,在每個位置建立蓄水場,那麼它影響的範圍一定是一段連續區間。證明不難。如果不是一段區間的話,假設\(x\)為其中的一個分割位置,因為有解,所以\(x\)必定能被\(x-1,x+1\)和\(x\)上一行這三個格子中的一個影響到,而\(x\)割開了一個線段,那麼必定跨越\(x\)這一列,所以不可能不影響到\(x\)位置。所以必定是一段連續區間。
那麼問題轉化成給定若干線段,選出最少的線段覆蓋所有區間,貪心即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define MAX 505
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int d[4][2]={1,0,-1,0,0,1,0,-1};
int a[MAX][MAX];
int n,m,tot,ans;
struct effect{int l,r;}p[MAX];
bool operator<(effect a,effect b){if(a.l!=b.l)return a.l<b.l;return a.r>b.r;}
bool vis[MAX][MAX];
namespace check
{
queue<int> Qx,Qy;
void bfs()
{
for(int i=1;i<=m;++i)Qx.push(1),Qy.push(i),vis[1][i]=true;
while(!Qx.empty())
{
int x=Qx.front(),y=Qy.front();Qx.pop(),Qy.pop();
for(int i=0;i<4;++i)
{
int xx=x+d[i][0],yy=y+d[i][1];
if(xx<1||yy<1||xx>n||yy>m)continue;
if(vis[xx][yy])continue;
if(a[xx][yy]>=a[x][y])continue;
vis[xx][yy]=true;
Qx.push(xx);Qy.push(yy);
}
}
}
void work()
{
bfs();int sum=0;
for(int i=1;i<=m;++i)
if(!vis[n][i])++sum;
if(sum){printf("0\n%d\n",sum);exit(0);}
puts("1");
}
}
namespace Get
{
void bfs(int Sx,int Sy)
{
memset(vis,0,sizeof(vis));queue<int> Qx,Qy;
Qx.push(Sx);Qy.push(Sy);vis[Sx][Sy]=true;
while(!Qx.empty())
{
int x=Qx.front(),y=Qy.front();Qx.pop(),Qy.pop();
for(int i=0;i<4;++i)
{
int xx=x+d[i][0],yy=y+d[i][1];
if(xx<1||yy<1||xx>n||yy>m)continue;
if(vis[xx][yy])continue;
if(a[xx][yy]>=a[x][y])continue;
vis[xx][yy]=true;
Qx.push(xx);Qy.push(yy);
}
}
}
void work()
{
for(int i=1;i<=m;++i)
{
bfs(1,i);int mx=0,mn=1e9;
for(int j=1;j<=m;++j)
if(vis[n][j])mx=j,mn=mn<j?mn:j;
if(mx<mn)continue;
p[++tot]=(effect){mn,mx};
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
a[i][j]=read();
check::work();Get::work();
sort(&p[1],&p[tot+1]);
int sum=0,r=0;
for(int i=1;i<=tot;++i)
if(p[i].r>p[i-1].r)p[++sum]=p[i];
tot=sum;
for(int i=1,j;i<=tot;i=j)
{
int nr=r;j=i;
while(j<=tot&&p[j].l<=r+1)nr=max(nr,p[j++].r);
ans+=1;r=nr;
}
printf("%d\n",ans);
return 0;
}