【基础练习】【单调队列和其他】10.19.2015校内测试总结

虽然最后也没有写完而且不想写了 但还是写一下题解 会有用的

题目:http://www.doc88.com/p-1196564818693.html 大概是这样


1.集卡片

用一个单调队列(或者说不重复队列)扫一遍即可。对于每个新扫到的元素,入队前先检查他是否与队首的元素相同,如果相同,那么由于我们要求种类齐全的情况下花费最少,队首元素就是多余的了,把队首所有相同元素pop掉,然后新元素入队。在这个过程中,ty变量统计队列内部元素种类,同时用一个int数组记录队列内每种元素有多少个。

如果某时刻发现队内元素种类恰好已包含所有种类,那么这就可能是一个可行解。这时,由于要求连续的序列且花费最少,在队首元素个数大于1时把队首的元素都出队,留下的就是尽量少但又种类齐全的“必需元素”了,此时检查结果是否更优即可。

代码:

//card 
//copyright by ametake
#include
#include
#include
#include
using namespace std;

const int maxn=1000000+10;
const int maxm=2000+10;
const int oo=0x3f3f3f;

int n,m,v;
int t[maxm];
int app[maxm];
int ty=0,price=0,ans=-oo;
queue q;

inline int read()
{
    int a=0;
    char ch=getchar();
    while (ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        a=a*10+ch-'0';
        ch=getchar();
    }
    return a;
}

int main()
{
    //freopen("1.txt","r",stdin);
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    n=read();
    m=read();
    v=read();
    for (int i=1;i<=m;i++)
    {
        t[i]=read();
    }
    int now;
    for (int i=1;i<=n;i++)
    {
        now=read();
        while (!q.empty()&&q.front()==now)
        {
            q.pop();
            price-=t[now];
            app[now]--;
            if (app[now]==0) ty--;
        }
        q.push(now);
        price+=t[now];
        if (!app[now])
        {
            ty++;
        }
        app[now]++;
        if (ty==m)
        {
            while (app[q.front()]>1)
            {
                app[q.front()]--;
                price-=t[q.front()];
                q.pop();
            }
            ans=max(v-price,ans);
        }
    }
    if (ans<0) printf("NO ans");
    else printf("%d",ans);
    return 0;
}
//15542 but 15396

2.天堂

由于每一层是各自独立的,每一层单独处理即可。但这道题目丧心病狂输入数据非要让你没办法在线处理,必须先读入完数据。

对于每一层,f[i,j] 表示第i步,走到第j个点的最优情况。

方程显然f[i,j]:=min{f[i-1,k]+dist(k,j)}

三重循环枚举即可。注意体重小于浮力直接上浮;还有就是如果多个店在同一点上这一点可以拿任意多的气球,不过这不影响DP

代码有严重错误,还未找出,只过了五个点,在这里就不放上了


3.环游世界

这道题目我退了一上午自以为正确但是只过了五个点,而且周围人还纷纷态度不太好,弄得我一下午心情很低落。我至今坚持认为我的思想是正确的,虽然程序出了错误,但我想那是因为DP初始状态没有考虑周到。

这道题其实我也是做麻烦了,很简单的用数学做就可以了。考虑图中只存在链和独立的点。把每条连当做一个点,对所有点求一次全排列个数(即阶乘)即可。而由于每条链有正反两种接入方式,每条链都会使方案数乘二,因此对链的个数做一次快速幂 二的幂次方 再相乘即可


//travel
//copyright by ametake
#include
#include
#include
using namespace std;

const int maxn=50+10;
const int mo=1000000007;
int n;
int have[maxn][3];
bool visited[maxn];
long long lian,lidi,ans=0;
int until;
bool flag=false;

inline int read()
{
    int a=0;
    char ch=getchar();
    while (ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        a=a*10+ch-'0';
        ch=getchar();
    }
    return a;
}

void dfs(int x)//¿´¿´Ê²Ã´Ê±ºò¼ÓÉÏlian 
{/*
    if (visited[x])
    {
        flag=true;
        return;
    }*/
    visited[x]=true;
    lidi++;
    until++;
    if (have[x][0]==1&&until==1) dfs(have[x][1]);
    else if (have[x][0]==2)
    {
        if (visited[have[x][1]]&&visited[have[x][2]])
        {
            flag=true;
            return;
        }
        else if (visited[have[x][1]]) dfs(have[x][2]);
        else dfs(have[x][1]);
    }
    else if (until>1) lian++;
}


long long mi(long long a,long long b,long long c){
	long long ans=1;long long x,y,z;x=a;y=b;z=c;
	while (b>0){
		if (b & 1==1) ans=(ans*a)%c;
		a=(a*a)%c;
		b>>=1;
	}
	return ans;
}

long long mul(long long k)
{
    long long now=1;
    for (int i=2;i<=k;i++)
    {
        now=(now*i)%mo;
    }
    return now;
}

int main()
{
    //freopen("1.txt","r",stdin);
    //freopen("2.txt","w",stdout);
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    n=read();
    char c[maxn];
    for (int i=1;i<=n;i++)
    {
        scanf("%s",c);
        for (int j=0;j2)
                {
                    printf("0");
                    return 0;
                }
                have[i][have[i][0]]=j+1;
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        if (have[i][0]==1&&(!visited[i]))
        {
            until=0;
            dfs(i);
        }
        if (flag)
        {
            printf("0");
            return 0;
        } 
    }
    for (int i=1;i<=n;i++) if (!visited[i]&&have[i][0]>=1)
    {
        printf("0");
        return 0;
    }
    n=n-lidi+lian;
    ans+=mi(2,lian,mo);
    ans*=mul(n);
    printf("%lld",ans%mo);
    return 0;
}


4.H1N1

这道题目我并没有仔细看···但据说是和很久以前的一道取水的图论相似···

然而听了里奥神犇讲解还是不会= =于是奥神发来了题解,于是我可耻地把题解粘到这儿来了

看口气应该是奥神自己写的···版权应该是奥神所有,再次表示感谢:

要达到一眼出解的熟练度。
先缩小问题规模,比如不考虑可以建设防疫站的城市。
如果只有一个有防疫站的城市,任务就是把所有点用最小的代价连通。说白了,求最小生成树。
然而题目中有防疫站的城市太多(超过了一个),所以想办法把防疫站缩为一个。我们可以设一个超级城市,让它有防疫站,让所有有防疫站的城市和它都有权值为0的无向边,那么原来有防疫站的城市本质上都变成了这一个城市。
其余城市按照题目给出的邻接矩阵连边,如果有公路则边权为零,否则为建造公路的代价。那如果没有可建造防疫站的城市,答案就是改造过的新图的最小生成树总权值。
现在考虑可以建防疫站的城市,初看似乎有困难……
超级城市有防疫站,又和所有有防疫站的城市有着权值为零的边,所以可以让原来有防疫站的城市没有防疫站。。。
我想说什么?
其实,其实就是如果想让某一个城市有防疫站,就可以让这个城市向超级城市连一条边,权值是让这个城市有防疫站的代价。一开始有防疫站的城市不需要任何代价就可以有防疫站,所以它们向超级城市连边的边权是零;而可以建造防疫站的城市有防疫站的代价就是在该城市建造防疫站的花费,所以只要以此花费为边权从该城市向超级城市连边,就处理好了这一类城市。
答案就是按上述方法构成的图的最小生成树的权值。



显然我没有代码···


所以,其实这几道题目看了题解都挺简单的,但是就是想不到。还是应该更多的锻炼自己的思维能力,形成一眼就知道模型的熟练度。还是要多看、多想、多练。


NOIP2015 加油

明天就是重阳节了。玉枕纱橱,半夜凉初透。

——天与秋光,转转情伤,探金英知近重阳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章