题目大意
有
n
n
n名候选人,
m
m
m个投票站,
每名候选人的得票总数为每个投票站得票数的和。
一名候选人能够当选,当且仅当他的总得票数严格大于其余的所有候选人。
现在已知每名候选人在每个投票站的得票情况,每个投票站的得票数不会超过
1
0
3
10^3
103。
问至少要删掉多少个投票站才能不让第
n
n
n名候选人当选。
需要输出具体删掉的投票站,多种方案输入任意一种方案即可。
时间限制
3s
数据范围
n , m ≤ 100 n,m\le100 n,m≤100
题解
为了不让第
n
n
n位候选人当选,只需要前面的
n
−
1
n-1
n−1位中的任意一位的得票数超过他即可。
于是就可以枚举前面的哪一位候选人超过他,这样
n
n
n个人比较的问题就转化成了
2
2
2个人比较的问题,处理起来就会方便很多。
考虑如何设状态,
如果设
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i个投票站中,使得两名候选人的得票为
j
j
j所需要的最多投票站的数量。
看上去非常好转移,但是不难发现这个状态数实在太多了,不可能全部记下来,而且也会TLE。
不过可以看出来,
f
i
,
j
f_{i,j}
fi,j的数值很小,不会超过
m
m
m,于是不妨换一下,设状态
g
i
,
j
g_{i,j}
gi,j表示前
i
i
i个投票站,选了
j
j
j个投票站,两名候选人的得票数差最大为多少。
转移也非常简单:
g
i
,
j
=
∑
k
=
0
i
−
1
m
a
x
(
g
k
,
j
−
1
+
选
第
i
个
投
票
站
产
生
的
得
票
数
差
值
,
g
i
−
1
,
j
)
g_{i,j}=\displaystyle\sum_{k=0}^{i-1} max \pod{g_{k,j-1}+ 选第i个投票站产生的得票数差值 , g_{i-1,j}}
gi,j=k=0∑i−1max(gk,j−1+选第i个投票站产生的得票数差值,gi−1,j)
因为要输出方案,因此在转移的时候,需要记录一下每个状态是从哪一个状态转移过来的,在最后统计答案的时候还原回去。
Code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <queue>
#define G getchar
#define ll long long
using namespace std;
int read()
{
char ch;
for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
int n = 0 , w;
if (ch == '-')
{
w = -1;
ch = G();
} else w = 1;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n * w;
}
const int N = 103;
int n , m , a[N][N];
int f[N][N] , g[N][N];
int ans , z[N];
bool bz[N];
void work (int x)
{
memset(f , 128 , sizeof(f));
f[0][0] = 0;
for (int i = 1 ; i <= m ; i++)
{
f[i][0] = 0;
for (int j = 1 ; j <= i ; j ++)
for (int k = j - 1 ; k < i ; k++)
{
if (f[k][j - 1] == f[0][1]) continue;
if (f[i][j] < f[k][j - 1] + a[i][x] - a[i][n])
{
f[i][j] = f[k][j - 1] + a[i][x] - a[i][n];
g[i][j] = k;
}
}
}
}
int main()
{
//freopen("c.in","r",stdin);
//freopen("c.out","w",stdout);
n = read();
m = read();
for (int i = 1 ; i <= m ; i++)
for (int j = 1 ; j <= n ; j++)
a[i][j] = read();
ans = 0;
for (int i = 1 ; i < n ; i++)
{
work(i);
for (int k = m ; k ; k--)
for (int j = k ; j > ans ; j--)
if (f[k][j] >= 0)
{
ans = j;
int pos = k;
for (int w = 1 ; w <= ans ; w++)
{
z[w] = pos;
pos = g[pos][j - w + 1];
}
break;
}
}
printf("%d\n", m - ans);
memset(bz , 1 , sizeof(bz));
for (int i = 1 ; i <= ans ; i++)
bz[z[i]] = 0;
for (int i = 1 ; i <= m ; i++)
if (bz[i]) printf("%d ", i);
return 0;
}