牛客国庆集训派对Day2: E. 数据排序(状压DP+记忆化搜索)

 

E. 数据排序

题目描述

机器学习通常需要用到大量的人工标注好的数据进行训练。现在有这么一个数据集,有 N 个张照片,每张照片中都有一个模特。某个研究员想要训练一个机器学习算法,能够根据照片对模特的魅力值进行评分。为了完成这个算法,研究员找了若干个志愿者对数据做一个标注。每个志愿者每次会看到系统给出的两张照片 x 和 y,然后告诉系统他认为哪张照片的魅力值更高。例如 x 的魅力值比 y 的要高(记作 <x, y>)这样一个有序二元组称之为一个数标注。
研究员收集了若干个这样的数据标注,他想找到一组对每张照片的评分 c1, ..., cn,使得这个评分和数据的冲突越少越好。为了方便设定N 张照片所组成的 对照片都分别有 4 个记录,也就是被标注了 4 次。定义 g(x, y) 为记录<x, y>出现的次数,定义评分 {cn} 的冲突值:

你需要求出在这个数据集下冲突值 f(c) 的最小值。

输入描述:

第一行一个整数 N,表示数据集大小。
接下来 2N(N-1) 行,每一行都有两个整数 xi, yi ,表示第 i 组数据标注 < xi, yi >.

输出描述:

输出一个整数,冲突值的最小值。

输入

2
1 2
1 2
2 1
1 2

输出

1

 

这题是2018吉林CCPC的I题的超级加强版

设dp[x]表示x状态下(x中所有为1的位一定比x中所有为0的位的分数高)的最优解

那么对于当前状态x,可以暴力枚举其所有子集p(p中所有位分数都相同)的情况下哪个最优

然后整体记忆化搜索就好了

暴力枚举所有子集复杂度是O(3^n)的,不过考虑每次可能还要求出集合两两之间的输赢分数总和(当然需要前缀和优化),复杂度实际上为O(3^n*n)

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<stdlib.h>
#include<string>
#include<math.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
int n, dp[32788], sc[18][18][5], pur[32788], cnt[32788], lose[16][32788];
int Sech(int x)
{
	int i, temp, now, win;
	if(dp[x]!=-1)
		return dp[x];
	temp = x;
	dp[x] = pur[x];
	while(temp)
	{
		temp = (temp-1)&x;
		win = x^temp;
		now = pur[win];
		for(i=0;i<=n-1;i++)
		{
			if((x&temp)&(1<<i))
				now += lose[i+1][win];
		}
		if(now<dp[x])
			dp[x] = min(dp[x], now+Sech(temp));
	}
	return dp[x];
}
int main(void)
{
	int i, j, x, y, k, sum;
	scanf("%d", &n);
	memset(dp, -1, sizeof(dp));
	for(i=1;i<=n*2*(n-1);i++)
	{
		scanf("%d%d", &x, &y);
		sc[x][y][0]++, sc[y][x][2]++;
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
			sc[i][j][1] = abs(sc[i][j][2]-sc[i][j][0]);
	}
	for(i=1;i<(1<<n);i++)
	{
		sum = 0;
		for(j=0;j<=n-1;j++)
		{
			if(i&(1<<j))
			{
				for(k=0;k<=j-1;k++)
				{
					if(i&(1<<k))
						sum += sc[j+1][k+1][1];
				}
			}
		}
		pur[i] = sum;
	}
	for(i=1;i<=n;i++)
	{
		for(j=0;j<(1<<n);j++)
		{
			for(k=0;k<=n-1;k++)
			{
				if(j&(1<<k))
					lose[i][j] += sc[i][k+1][0];
			}
		}
	}
	printf("%d\n", Sech((1<<n)-1));
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值