题目大意
有nnn名候选人,mmm个投票站,
每名候选人的得票总数为每个投票站得票数的和。
一名候选人能够当选,当且仅当他的总得票数严格大于其余的所有候选人。
现在已知每名候选人在每个投票站的得票情况,每个投票站的得票数不会超过10310^3103。
问至少要删掉多少个投票站才能不让第nnn名候选人当选。
需要输出具体删掉的投票站,多种方案输入任意一种方案即可。
时间限制
3s
数据范围
n,m≤100n,m\le100n,m≤100
题解
为了不让第nnn位候选人当选,只需要前面的n−1n-1n−1位中的任意一位的得票数超过他即可。
于是就可以枚举前面的哪一位候选人超过他,这样nnn个人比较的问题就转化成了222个人比较的问题,处理起来就会方便很多。
考虑如何设状态,
如果设fi,jf_{i,j}fi,j表示前iii个投票站中,使得两名候选人的得票为jjj所需要的最多投票站的数量。
看上去非常好转移,但是不难发现这个状态数实在太多了,不可能全部记下来,而且也会TLE。
不过可以看出来,fi,jf_{i,j}fi,j的数值很小,不会超过mmm,于是不妨换一下,设状态gi,jg_{i,j}gi,j表示前iii个投票站,选了jjj个投票站,两名候选人的得票数差最大为多少。
转移也非常简单:gi,j=∑k=0i−1max(gk,j−1+选第i个投票站产生的得票数差值,gi−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;
}


350

被折叠的 条评论
为什么被折叠?



