noip2010提高组

    最近几次始终考的不怎么好,思路比较死。

---------------------------------------------------------------------------------------------------------------------------


    一道简单的模拟,不多说,用stl很简单直观

代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cctype>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
#define ll long long
#define in(x) scanf("%d",&x)
#define lin(x) scanf("%lld",&x)
#define out(x) printf("%d",x)
#define lout(x) printf("%lld",x)
#define ko putchar(' ')
#define ex putchar('/n')
const int MAXN = 1e3 + 5;
int n,m;
set<int> s;
queue<int> q;
int cnt = 0,ans = 0;

int main()
{
//	freopen("translate.in","r",stdin);
//	freopen("translate.out","w",stdout);
	in(m);in(n);
	for(int i = 1;i <= n;i++)
	{
		int t;
		in(t);
		if(s.empty() || !s.count(t))
		{
			if(cnt >= m)
			{
				int now = q.front();
				q.pop();
				s.erase(now);
			}
			s.insert(t);
			q.push(t);
			cnt++;
			ans++;
		}
	}
	out(ans);
	return 0;
}
    很明显的动规题,一开始想到的状态是:f[i]代表当走到第i个格子时的最大分数。

    但后来分析一下题意发现卡片的消耗导致这种状态不能完成此题(因为这么定义无法判定卡片怎么消耗)

    想了很久没想到,只好打了个深搜暴力放着。

    考后看到std才知道原来状态是围绕卡片定义的:f[i][j][k][l]代表用了i张1步,j张2步,k张3步,l张4步。

    而当前格数就用r = 1(一开始就在第一格) + i + j*2 + k*3 + l*4 算出来的!

    想来是个不难的简单动规,可惜想到的人寥寥无几。

    代码如上所述:

#include<bits/stdc++.h>
using namespace std;
#define RG register
#define ll long long
#define in(x) x = read()
#define lin(x) scanf("%lld",&x)
#define llin(x) x = lread
#define din(x) scanf("%lf",&x)
#define dout(x) printf("%lf",x)
#define out(x) print(x)
#define lout(x) printf("%lld",x)
#define ex putchar('\n') 
#define ko putchar(' ')
const ll p = 1e10 + 7;
const int MAXN = 351;
int n,m;
int w[MAXN];
int f[41][41][41][41];
int v[MAXN];
inline int read() 
{
    int X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

inline ll lread()  
{
    ll X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=((X<<3)+(X<<1)+ch-'0')%p,ch=getchar();
    return X*w;
}

void print(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        print(x/10);
    }
    putchar(x%10+'0');
}

int main()
{
	in(n);in(m);
	for(int i = 1;i <= n;i++)
		in(v[i]);
	f[0][0][0][0] = v[1];
	for(int i = 1;i <= m;i++)
	{
		int t;
		in(t);
		w[t]++;
	}
	for(int a = 0;a <= w[1];a++)
	{
		for(int b = 0;b <= w[2];b++)
		{
			for(int c = 0;c <= w[3];c++)
			{
				for(int d = 0;d <= w[4];d++)
				{
					int r = 1+a+b*2+c*3+d*4;
					if(a != 0) f[a][b][c][d] = max(f[a][b][c][d],f[a-1][b][c][d] + v[r]);
					if(b != 0) f[a][b][c][d] = max(f[a][b][c][d],f[a][b-1][c][d] + v[r]);
					if(c != 0) f[a][b][c][d] = max(f[a][b][c][d],f[a][b][c-1][d] + v[r]);
					if(d != 0) f[a][b][c][d] = max(f[a][b][c][d],f[a][b][c][d-1] + v[r]);
				}
			}
		}
	}
	out(f[w[1]][w[2]][w[3]][w[4]]);	
	return 0;
}

    这道题用到并查集+二分图染色思想(只用了思想没用任何相关算法)。

    个人来说没想到并查集,导致模拟贪心直接爆炸。

    总而言之,这题就类似于某道名为食物链的题,只是多了贪心要最小值。

    贪心主要就是每次都拆怨气最大的俩位狱♂友,知道不行了肯定就上报那个怨♂气最大的嘛。

    1、看到代码就知道,我把并查集多开了n个节点,相当于俩监狱。

    2、priority_queue,最小堆来贪心。

    3、并查集操作,都把现在点祖先赋为另一个监狱的点,可以想到发现俩点祖先为一个时说明肯定两人只能在同一个监狱了,于是输出(因为贪心最小堆的缘故找到一个肯定是接下来中最大的了直接输出结束程序就好)。

    4、特判,没有怨气就输出0。

    代码中还是有些细节说不太清,大家看看吧。

#include<bits/stdc++.h>
using namespace std;
#define RG register
#define ll long long
#define in(x) x = read()
#define lin(x) scanf("%lld",&x)
#define llin(x) x = lread
#define din(x) scanf("%lf",&x)
#define dout(x) printf("%lf",x)
#define out(x) print(x)
#define lout(x) printf("%lld",x)
#define ex putchar('\n') 
#define ko putchar(' ')
const ll p = 1e10 + 7;
const int MAXN = 1e5 + 5;
int n,m;
inline int read() 
{
    int X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

inline ll lread()  
{
    ll X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=((X<<3)+(X<<1)+ch-'0')%p,ch=getchar();
    return X*w;
}

void print(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        print(x/10);
    }
    putchar(x%10+'0');
}

struct NODE
{
	int x,y,val;
};

struct cmp
{
	bool operator()(const NODE& a,const NODE& b)
	{
		return a.val < b.val;
	}
};

priority_queue<NODE,vector<NODE>,cmp>gx;

int ant[MAXN]; 
int Find(int x)
{
	if(ant[x] != x)
		return ant[x] = Find(ant[x]);
	return x;
} 
int main()
{
	in(n);in(m);
	for(int i = 1;i <= 2*n;i++) ant[i] = i;
	for(int i = 1;i <= m;i++)
	{
		int a,b,c;
		in(a);in(b);in(c);
		gx.push((NODE){a,b,c});
	}
	while(!gx.empty())
	{
		if(Find(gx.top().x) == Find(gx.top().y))
		{
			out(gx.top().val);
			return 0;
		}
		int tx = gx.top().x;int ty = gx.top().y;
		ant[ant[tx]] = ant[Find(ty+n)];
		ant[ant[ty]] = ant[Find(tx+n)];
		gx.pop();
	}
	cout << 0;
	return 0;}


    一道搜索题(虽然可以优化为动规但是其实记忆化深搜和广搜都能过。。)

    总的来说,只要能实现判断第一行到最后一行的算法就都行。

    实际上这道题铺水的(可以理解为染色吧,能输到水的就是被染了色)最后一行的线段一定是切合在一起的,(如果不这样的话很明显看出中间会差一截不能完成。)所以我们可以就此用一个动规思想得到每个点能到的最左和最右,从而得到最后一行的最左和最右。

    然后最后遍历一下最后一行就行。

具体还是看代码吧,毕竟实现方法很多,这是其中一种罢了:(注意宏定义,比较懒)

#include<bits/stdc++.h>
using namespace std;
#define RG register
#define ll long long
#define in(x) x = read()
#define lin(x) scanf("%lld",&x)
#define llin(x) x = lread
#define din(x) scanf("%lf",&x)
#define dout(x) printf("%lf",x)
#define out(x) print(x)
#define lout(x) printf("%lld",x)
#define ex putchar('\n') 
#define ko putchar(' ')
const ll p = 1e10 + 7;
const int MAXN = 505; 
#define addx x + xx[i]
#define addy y + yy[i]
int xx[4] = {-1,0,1,0};
int yy[4] = {0,1,0,-1};
int n,m;
int cnt = 0;
int h[MAXN][MAXN];
int l[MAXN][MAXN],r[MAXN][MAXN];
bool vis[MAXN][MAXN]; 
bool flag = 0; 
inline int read() 
{
    int X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

inline ll lread()  
{
    ll X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=((X<<3)+(X<<1)+ch-'0')%p,ch=getchar();
    return X*w;
}

void print(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        print(x/10);
    }
    putchar(x%10+'0');
}

void dfs(int x,int y)
{
	vis[x][y] = 1;
	for(int i = 0;i < 4;i++)
	{
		if(addx < 1 || addx > n || addy < 1 || addy > m) continue;
		if(h[addx][addy] >= h[x][y]) continue;
		if(!vis[addx][addy]) dfs(addx,addy);
		l[x][y] = min(l[x][y],l[addx][addy]);
		r[x][y] = max(r[x][y],r[addx][addy]);
	}
}

void work()
{
	for(int i = 1;i <= m;i++)
	{
		if(!vis[1][i]) dfs(1,i);
	}
	for(int i = 1;i <= m;i++)
		if(!vis[n][i])
		{
			flag = 1;
			cnt++;
		}
	if(flag)
	{
		cout << 0;
		ex;
		out(cnt);
		return;
	}
	int le = 1;
	while(le <= m)
	{
		int maxr = 0;
		for(int i = 1;i <= m;i++)
			if(l[1][i] <= le)
				maxr = max(maxr,r[1][i]);
		cnt++;
		le = maxr+1;
	}
	cout << 1;
	ex;
	out(cnt);
}


void init()
{
	in(n);in(m);
   	memset(vis,false,sizeof(vis));
	memset(l,0x3f,sizeof(l));
    memset(r,0,sizeof(r));	
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			in(h[i][j]);
	for(int i = 1;i <= m;i++)
		l[n][i] = r[n][i] = i;
}


int main()
{
	init();
	work();
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值