noi2009

变换序列

题目:

变换序列

分析:

二分图匹配。

至于字典序最小,从后往前进行匹配即可

程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <memory.h>
#include <fstream>

using namespace std;

const int Max_N = 10010;

int n;
int edge[Max_N][2];

void Init()
{
	scanf("%d", &n);
	for(int i = 0; i < n; i ++)
	{
		int x;
		scanf("%d", &x);
		edge[i][0] = (i + x) % n;
		edge[i][1] = (i + n - x) % n;
		if(edge[i][0] > edge[i][1])
			swap(edge[i][0], edge[i][1]);
	}
}

int flag[Max_N];
bool visited[Max_N];

bool Dfs(int x)
{
	for(int i = 0; i < 2; i ++)
	{
		int y = edge[x][i];
		if(! visited[y])
		{
			visited[y] = true;
			if(flag[y] == -1 || Dfs(flag[y]))
			{
				flag[y] = x;
				return true;
			}
		}
	}
	return false;
}

int res[Max_N];
void Output()
{
	for(int i = 0; i < n; i ++)
		res[flag[i]] = i;
	for(int i = 0; i < n - 1; i ++)
		printf("%d ", res[i]);
	printf("%d\n", res[n - 1]);
}

void Solve()
{
	memset(flag, -1, sizeof(flag));
	int cnt = 0;
	for(int i = n - 1; i >= 0; i --)
	{
		memset(visited, false, sizeof(visited));
		if(Dfs(i))
			cnt ++;
	}
	if(cnt < n)
		printf("No Answer\n");
	else
		Output();
}

int main()
{
//	freopen("transform.in", "r", stdin);
//	freopen("transform.out", "w", stdout);
	Init();
	Solve();
	return 0;
}

诗人小G

waiting...

二叉查找树

题目:

分析:

动归题
分析题目可以知道,由于只能更改权值,而数据值大小不变,且整棵树满足儿子节点的数据值大于父节点的数据值。因此,不论权值如何修改,整棵树从左到右的排列顺序不会改变。
于是,
(1)将给定的节点先按权值排序,并进行离散化。然后按数据值排序。
(2)设 dp[ i, j, k ] 表示排序后可用来表示这颗树的数列在区间 [ i, j ] 中,所有权值都大于 k 的最小访问代价。
易知,dp [ i, j, k ] = min (dp[ i, u - 1, k ] + dp[ u + 1, j, k ] + K + sum [ j ] - sum [ i - 1 ],dp[ i, u - 1, k ] + dp[ u + 1, j, k ] + sum[ j ] - sum[ i - 1 ])
其中,u 表示所枚举出的在区间 [ i, j ] 中的根。sum [ i ] 表示 1~ i 的频度总和
前一种表示 u 这个节点的权值小于 k,因此,需要调整。
后一种表示 u 这个节点的权值大于 k,因此,不需要调整。
因为树的访问代价包括 sigma ( 频度 X 深度 )。又因为 dp 是由下向上一层层更新,这样每次加上 sum [ j ] - sum [ i - 1],可以保证 sigma 的正确性

程序

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <memory.h>

using namespace std;

#define LL long long

const int Max_N = 80;
const int inf = 987654321;

struct Tree
{
    int data, weight, freq;
}tree[Max_N];

int n, ext;
int sum[Max_N];

bool Cmp1(Tree a, Tree b)
{
    return a.data < b.data;
}

bool Cmp2(Tree a, Tree b)
{
    return a.weight < b.weight;
}

void Init()
{
    scanf("%d%d", &n, &ext);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &tree[i].data);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &tree[i].weight);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &tree[i].freq);

    sort(tree + 1, tree + n + 1, Cmp2);
    for(int i = 1; i <= n; i ++)
        tree[i].weight = i;
    sort(tree + 1, tree + n + 1, Cmp1);

    sum[0] = 0;
    for(int i = 1; i <= n; i ++)
        sum[i] = sum[i - 1] + tree[i].freq;
}

LL dp[Max_N][Max_N][Max_N];

LL Dp(int i, int j, int k)
{
    if(i > j)
        dp[i][j][k] = 0;
    if(dp[i][j][k] > -1)
        return dp[i][j][k];
    for(int u = i; u <= j; u ++)
    {
        if(tree[u].weight >= k)
            if(dp[i][j][k] == -1 || dp[i][j][k] > Dp(i, u - 1, tree[u].weight) + Dp(u + 1, j, tree[u].weight))
                dp[i][j][k] = Dp(i, u - 1, tree[u].weight) + Dp(u + 1, j, tree[u].weight);
        if(dp[i][j][k] == -1 || dp[i][j][k] > Dp(i, u - 1, k) + Dp(u + 1, j, k) + ext)
            dp[i][j][k] = Dp(i, u - 1, k) + Dp(u + 1, j, k) + ext;
    }
    if(dp[i][j][k] > -1)
        dp[i][j][k] += sum[j] - sum[i - 1];
    return dp[i][j][k];
}

LL Dp()
{
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 0; j < i; j ++)
            for(int k = 0; k < Max_N; k ++)
                dp[i][j][k] = 0;
    }
    for(int l = 1; l <= n; l ++)
    {
        for(int i = 1; i + l - 1 <= n; i ++)
        {
            int j = i + l - 1;
            for(int k = 0; k <= n; k ++)
            {
                for(int u = i; u <= j; u ++)
                {
                    if(tree[u].weight >= k)
                        if(dp[i][j][k] == -1 || dp[i][j][k] > Dp(i, u - 1, tree[u].weight) + Dp(u + 1, j, tree[u].weight))
                            dp[i][j][k] = Dp(i, u - 1, tree[u].weight) + Dp(u + 1, j, tree[u].weight);
                    if(dp[i][j][k] == -1 || dp[i][j][k] > Dp(i, u - 1, k) + Dp(u + 1, j, k) + ext)
                        dp[i][j][k] = Dp(i, u - 1, k) + Dp(u + 1, j, k) + ext;
                }
                if(dp[i][j][k] > -1)
                    dp[i][j][k] += sum[j] - sum[i - 1];
            }
        }
    }
}

void Solve()
{
    memset(dp, -1, sizeof(dp));
//    printf("%lld\n", dp[1][n][0]);
    printf("%lld\n", Dp(1, n, 0));
}

int main()
{
    Init();
    Solve();
    return 0;
}

植物大战僵尸

题目:

分析:

网络流题:最大闭权子图
(1)我们根据每个植物的保护关系建立一张图。其中,每行中靠右的植物一定保护靠左的植物。
(2)我们发现,如果一些植物构成环。那么这一定是不能被僵尸吃掉的。建图把它们删掉。
(3)由于需要求出的是 score 总和最大。因此,如果把所建的边全部反向,会发现要求答案,只要求出最大闭权子图就可以了。因为闭合图是指图中每个点所指向的点也在图内,如果边没有反向,那么最左边的植物是必选的,但是右侧的点不一定选,与题意不符。而如果反向,那么最右边的植物是必选的,而较左侧的点可以不选,符合题意。

程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <memory.h>
#include <queue>
#include <fstream>

using namespace std;

const int Max_N = 610;
const int inf = ~0U >> 1;

int n, m;

struct Edge
{
    int y, flow, next;
}pvz[Max_N * Max_N * 2], edge[Max_N * Max_N * 5];
int len_e, root_e[Max_N];
int deg_in[Max_N], len_p, root_p[Max_N];

int s, t;
int score[Max_N * Max_N];

void Add_Edge_(int x, int y, int z)
{
	edge[len_e] = (Edge){y, z, root_e[x]};
	root_e[x] = len_e ++;
}

void Add_Edge(int x, int y, int z)
{
	Add_Edge_(x, y, z);
	Add_Edge_(y, x, 0);
}

void Add_Pvz(int x, int y)//contain the node who points to itself
{
	pvz[len_p] = (Edge) {y, 0, root_p[x]};
	root_p[x] = len_p ++;
	deg_in[y] ++;
}

int Point2Id(int x, int y)
{
    return x * m + y;
}

void Make_Pvz()//transposed graph
{
	memset(deg_in, 0, sizeof(deg_in));
	memset(root_p, -1, sizeof(root_p));
	len_p = 0;
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < m; j ++)
        {
            scanf("%d", &score[Point2Id(i, j)]);
            if(j + 1 < m)//(i, j) -> (i, j + 1)
            	Add_Pvz(Point2Id(i, j + 1), Point2Id(i, j));
//            	Add_Pvz(Point2Id(i, j), Point2Id(i, j + 1));

            int tmp;
            scanf("%d", &tmp);
            for(int k = 0; k < tmp; k ++)
            {
                int x, y;
                scanf("%d%d", &x, &y);//(x, y) -> (i, j)
                Add_Pvz(Point2Id(i, j), Point2Id(x, y));
//                Add_Pvz(Point2Id(x, y), Point2Id(i, j));
            }
        }
    }

    n = n * m;
}

bool choose[Max_N];

void Topo_Sort()
{
	queue <int> q;
	memset(choose, false, sizeof(choose));
	for(int i = 0; i < n; i ++)
		if(deg_in[i] == 0)
			q.push(i);
	while(!q.empty())
	{
		int x = q.front();
		q.pop();
		choose[x] = true;
		for(int p = root_p[x]; p != -1; p = pvz[p].next)
		{
			int y = pvz[p].y;
			deg_in[y] --;
			if(deg_in[y] == 0)
				q.push(y);
		}
	}
}

int remain;

void Make_Graph()
{
	s = n;
	t = s + 1;
	remain = 0;
	len_e = 0;
	memset(root_e, -1, sizeof(root_e));
	for(int i = 0; i < n; i ++)
	{
		if(choose[i])
		{
			if(score[i] > 0)
			{
				Add_Edge(s, i, score[i]);
				remain += score[i];
			}
			else
				Add_Edge(i, t, -score[i]);
			for(int p = root_p[i]; p != -1; p = pvz[p].next)
			{
				int y = pvz[p].y;
				if(choose[y])
					Add_Edge(y, i, inf);
			}
		}
	}
}

int d[Max_N];
int seq[Max_N], l, r;

bool Bfs()
{
	memset(d, 0, sizeof(d));
	l = r = 0;
	seq[r ++] = s;
	d[s] = 1;
	while(l < r)
	{
		int i = seq[l ++];
		for(int p = root_e[i]; p != -1; p = edge[p].next)
		{
			int y = edge[p].y;
			if(!d[y] && edge[p].flow > 0)
			{
				d[y] = d[i] + 1;
				seq[r ++] = y;
				if(y == t)
					return true;
			}
		}
	}
	return false;
}

int Dinic(int i, int flow)
{
	if(i == t)
		return flow;
	int res = flow;
	for(int p = root_e[i]; p != -1; p = edge[p].next)
		if(edge[p].flow > 0 && d[i] + 1 == d[edge[p].y])
		{
			int tmp = Dinic(edge[p].y, min(res, edge[p].flow));
			if(tmp == 0)
				d[edge[p].y] = 0;
			edge[p].flow -= tmp;
			edge[p ^ 1].flow += tmp;
			res -= tmp;
		}
	return flow - res;
}

void Init()
{
    scanf("%d%d", &n, &m);
    s = n * m;
    t = s + 1;
    Make_Pvz();
    Topo_Sort();
    Make_Graph();
}

void Solve()
{
    int ans = 0;
    while(Bfs())
	    ans += Dinic(s, inf);
	printf("%d\n", remain - ans);
}

int main()
{
/*
	freopen("pvz.in", "r", stdin);
	freopen("pvz.out", "w", stdout);
*/
	Init();
	Solve();
    return 0;
}


管道取珠

题目:

分析:

组合数学
设 f [ x1, y1, x2, y2 ] 表示分别采用两种方法X,Y同时得到相同的珠子的排列的个数。对于每一个结果A,它有 |X| * |Y| 中可能性。其中 |X| = |Y| = a [ i ]
因此,sigma (ai ^ 2) 就表示采用两种方法,经过各种步骤达到最终结果的总和

程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <memory.h>
 
using namespace std;
 
const int Max_N = 510;
const int Mod = 1024523;
 
int n_up, n_down;
char sign_up[Max_N], sign_down[Max_N];
 
void Init()
{
    scanf("%d%d", &n_up, &n_down);
    scanf("%s%s", sign_up + 1, sign_down + 1);
}
 
int dp[Max_N][Max_N][Max_N];
 
void Add(int i1, int j1, int i2, int delta)
{
    dp[i1][j1][i2] = (dp[i1][j1][i2] + delta) % Mod;
}
 
void Solve()
{
    memset(dp, 0, sizeof(dp));
    dp[0][0][0] = 1;
    for(int t = 0; t < n_up + n_down; t ++)
    {
        for(int i1 = 0; i1 <= t; i1 ++)
        {
            if(i1 > n_up)
                break;
            int j1 = t - i1;
            for(int i2 = 0; i2 <= t; i2 ++)
            {
                if(i2 > n_up)
                    break;
                int j2 = t - i2;
                int delta = dp[i1][j1][i2];
                if(!delta)
                    continue;
                if(sign_up[i1 + 1] == sign_up[i2 + 1])
                    Add(i1 + 1, j1, i2 + 1, delta);
                if(sign_up[i1 + 1] == sign_down[j2 + 1])
                    Add(i1 + 1, j1, i2, delta);
                if(sign_down[j1 + 1] == sign_up[i2 + 1])
                    Add(i1, j1 + 1, i2 + 1, delta);
                if(sign_down[j1 + 1] == sign_down[j2 + 1])
                    Add(i1, j1 + 1, i2, delta);
            }
        }
    }
    printf("%d\n", dp[n_up][n_down][n_up]);
}
 
int main()
{
    Init();
    Solve();
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值