G. Minimal Coverage
参考博客:shround777
题目大意:
多条线段,每条可以接在前一条的末尾,问最小覆盖长度。
写在前面:
一开始是想的通过二分右边界,来倒着贪心。(通过知道右边界来贪心选择线段是左旋还是右旋)
实际上这个思路有明显的后效性(模样例就可以发现,错误思路不过多叙述)
思路:
二分是很好想的一个方向,但这个
c
h
e
c
k
check
check函数不太好想。
二分答案(线段长在1000以内答案在2000以内,不太懂证明)
d
p
[
i
]
dp[i]
dp[i]表示
i
i
i位置可否成为下一个线段的起点(不考虑是否放的下)
因为要左旋和右旋考虑用
b
i
t
s
e
t
bitset
bitset来实现,下一个状态,就是左移和右移的并集。
int check(int len){
s=0,t=0; // 清空
for(int i=0;i<=len;i++){ // 长度是len,起点包含了0-len.
s[i]=1,t[i]=1; // 在这个长度内都可以作为起点(不考虑起点一定是0,在平移0对答案无影响)
}
for(int i=1;i<=n;i++){
s=((s<<a[i])|(s>>a[i]))&t; // &运算的目的是消除放不下的问题
}
return s.count(); // 区间是否存在合法终点
}
其实 c h e c k check check本质是把所有状态转移了一遍。
Code:
#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <bitset>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=1e4+10;
int n,a[N];
bitset<2010> s,t;
int check(int len){
s=0,t=0;
for(int i=0;i<=len;i++){
s[i]=1,t[i]=1;
}
for(int i=1;i<=n;i++){
s=((s<<a[i])|(s>>a[i]))&t;
}
return s.count();
}
int main(){
guo312;
int t; cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=1,r=2000;
while(l<r){
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
return 0;
}