版权声明:本文为博主原创文章,你们可以随便转载 https://blog.csdn.net/Jaihk662/article/details/82927404
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;
}