省赛训2

C:uva 11691 allergy test
贪心or状压dp
贪心思路:
利用优先队列,每次将最长的两段拿出来,尽可能的互相覆盖后把余下的部分(还能再放段落的部分)压回队列;
代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
priority_queue <int> q ;
int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        while(n--)
        {
            int x;
            cin >> x;
            q.push(x);
        }
        if(n==1)
        {
            int ans = q.top();
            q.pop();
            cout << ans << endl;
            continue;
        }
        int ans=0;
        while(!q.empty())
        {
            int a=q.top();
            q.pop();
            int b=q.top();
            q.pop();
            if(q.empty())
            {
                ans+=a+1;//只剩两个说明压完了,因为第一个还没压进去,所以现在压进去
                cout << ans << endl;
                break;
            }
            ans+=b;
            q.push(a-b+1);//多余的部分再压回去
        }

    }
    return 0;
}

状压:
其实就是暴力,枚举所有可能的排列,删去重复的情况,用二进制数存放状态,仅此而已。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f;
const int N=25;
const int M=(1<<20)+5;
int n;
int dp[M][8],num[N];
int dfs(int s,int k)            //s表示状态,k表示上一次操作后剩余的过敏源独立存在的时间数(可以用来存放下一个过敏原);
{
    if(dp[s][k]!=-1)return dp[s][k];
    dp[s][k]=inf;
    if(s==(1<<n)-1)return dp[s][k]=0;
    int vis[8];
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++)
    {
        if(vis[num[i]])continue;              //同一种时间长度效果一致
        if(s&(1<<i))continue;                 //已经放过的不放
        vis[num[i]]=1;
        int t = dfs(s^(1<<i),max(0,num[i]-k-1))+max(1,num[i]-k);      //前一个max计算k,后一个max表示放置后的长度。
        dp[s][k] = min(dp[s][k],t);
    }
    return dp[s][k];
}
int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        for(int i=0;i<n;i++)cin >> num[i];
        memset(dp,-1,sizeof(dp));
        cout << dfs(0,0) << endl;
    }
    return 0;
}

G uva 11695 Flaght Planning
树的直径:两遍dfs即可找到一棵树的直径。
该题的做法是枚举每一条边都删除一次,删除后把两棵子树的中点连起来,再找连起来的整个树的直径,取直径最小的情况。
实际操作不会连中点(太麻烦,我们可以直接算出连中点后的树的直径(=两棵子树最长路径相连)(该题特殊性,删边后应该是只会生成两个子树,而不是还是一棵树,但就算还是一棵树计算方法也是一样的。))
连中点后树的直径=max((d1+1)/2+(d2+1)/2+1,d1,d2);
该题要求输出加边的结点,那么就记录一下路径,直接取路径的中点就好了。
代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <array>
#include <cstring>
using namespace std;
typedef pair<int, int>PII;
const int inf = 0x3f3f3f;
const int N = 2510;
int  cnt, len, fp, mid, fa,vis[N];
int head[N];
array <int, N> path;
struct Edge
{
	int from, to, nxt;
	bool hs_del;
}edges[2 * N];
void added(int from, int to)        //加边
{
	Edge& x = edges[cnt];
	x.from = from;
	x.to = to;
	x.nxt = head[from];
	x.hs_del = 0;
	head[from] = cnt++;
}
void dfs(int st, int step)              
{
	vis[st] = 1;                 //不走回头路
	path[step] = st;              //记录路径
	if (step > len)
	{
		len = step;
		fp = st;
		mid = path[ len / 2];            //记录路径中点
	}
	for (int i = head[st]; ~i; i = edges[i].nxt)          //用链表的方式来存储和遍历.
	{
		if (edges[i].hs_del)continue;
		int v = edges[i].to;
		if (vis[edges[i].to])continue;
		dfs(v, step + 1);
	}
}
int fun(int st)
{
	len = 0;
	fp = st;
	memset(vis, 0, sizeof(vis));
	mid = st;
	dfs(st, 0);
	int st1 = fp;
	len = 0;
	memset(vis, 0, sizeof(vis));
	dfs(st1, 0);
	return len;
}
int main()
{
	ios::sync_with_stdio(false);
	int T;
	scanf("%d", &T);
	while (T--)
	{
		memset(head, -1, sizeof(head));
		int n, faa_f, faa_t, fdel_f, fdel_t;
		cnt = 0;
		int mfnow = 10000000;
		scanf("%d", &n);
		for (int i = 1; i < n; i++)
		{
			int f, t;
			scanf("%d%d", &f, &t);
			added(f, t);
			added(t, f);
		}
		for (int i = 0; i < cnt; i += 2)
		{
			int fr = edges[i].from, tt = edges[i].to;
			edges[i].hs_del = true;
			edges[i ^ 1].hs_del = true;
			int d1 = fun(fr), fa1 = mid;
			int d2 = fun(tt), fa2 = mid;
			int d3 = max(d1 / 2, d1 - d1 / 2) + max(d2 / 2, d2 - d2 / 2)+1;
			d1 = max(d3, max(d2, d1));
			if (d1 < mfnow)
			{
				mfnow = d1;
				fdel_f = edges[i].from;
				fdel_t = edges[i].to;
				faa_f = fa1;
				faa_t = fa2;
			}
			edges[i].hs_del = false;
			edges[i ^ 1].hs_del = false;
		}
		printf("%d\n%d %d\n%d %d\n", mfnow, fdel_f, fdel_t, faa_f, faa_t);
	}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值