https://ac.nowcoder.com/acm/contest/893#question
Problem A
题意:
题解:模拟
C++版本一
#include<bits/stdc++.h>
using namespace std;
int main(){
int flag = 0;
int n1,n2 , p1,p2 , s1,s2;
cin >> n1 >> p1 >> s1;
cin >> n2 >> p2 >> s2 ;
if(n1 > n2) flag = 1;
else if(n2 > n1) flag = 0;
else
{
if(p1 > p2) flag = 0;
else if(p2 > p1) flag = 1;
else
{
if(s1 > s2) flag = 0;
else if(s1 < s2) flag = 1;
else
{
flag = 2;
}
}
}
if(flag == 1) cout <<1 << endl;
else if(flag == 2) cout << "God" << endl;
else cout << 2 << endl;
}
Problem B
题意:
题解:模拟
C++版本一
#include<bits/stdc++.h>
using namespace std;
int main(){
int t ;
cin >> t;
while(t--){
int sum = 0;
int n ;
cin >> n;
while(n > 1){
if(n % 10 == 0)
n/=10,sum++;
else {
n++;
sum ++;
}
}
cout << sum << endl;
}
}
Problem C
题意:
证明a % 192 = 1
后面就是等差数列和
题解:
C++版本一
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
long long l , r;
cin >> l >> r;
long long len2 = (r -1)/ 192 ;
long long len1 = (l-2) / 192;
//if(r - 1<= 192) len2 = 0;
long long sum2 = len2+1 + (96 + 96 * len2) * len2 ;
//if(l - 1<= 192) len1 = 0;
long long sum1 = len1+1 + (96 + 96 * len1) * len1;
if(l-1 == 0) sum1 = 0;
cout << sum2 - sum1 << endl;
}
}
Problem D
题解:
贪心 题目乍一看像是NIM的模板题或者DP的模板题 但其实。。。 石子数累加和-最大一堆的石子数
因为每次合并代价都是小堆石子的数量,不妨设三堆石子a<= b <= c 最优方案,必然是a合并到c,再b合并到a+c;
如果先合并a和b,那么代价必然大于a+b 推而广之,最优方案为每次都往最大的那个堆上合并。 时间复杂度O(n)
C++版本一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//std::vector<int> a;
int a[2000000 + 7];
int main(){
int t;
cin >> t;
while(t--){
//a.clear();
int n ;
cin >> n ;
for(int i = 0; i < n ; i++ ) cin >> a[i];
//sort(a.begin() , a.end());
sort(a,a+n);
ll sum = 0;
for(int i = 0; i < n - 1 ; i++){
sum += a[i ];
}
cout << sum << endl;
}
}
Problem E
题解:
两点 写法简单一点是O(m),复杂一点是O(n),两种都可以过 O(m)的方法:循环维护到每个人吃西瓜时的西瓜数量的区间[L,R]
初始L=R=m 对于非肚量最大的人L每次减一(至少吃一个),R每次减去能ai 对于肚量最大的人L和R都减去a[i]
直到某个非肚量最大的人吃西瓜的时候如果L<0,得到答案为No 或者肚量最大的人吃西瓜时R<0,那么说明可以成功,得到答案为Yes
O(n)方法类似,求出肚量最大的人之前和之后的人的肚量之和即可优化。
C++版本一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[2000000 + 1];
int main()
{
int t;
cin >> t;
while(t--)
{
int n, m ;
cin >> n >> m;
int sum = 0,index = 0;
for(int i = 1; i <= n ; i++)
{
cin >> a[i];
//sum += a[i];
if(a[i] > sum) sum = a[i],index = i;
}
//cout << index << endl;
bool flag = 0;
int ans = 0;
//for(int i = 1; i < index ; i++) ans += a[i];
int l , r;
l = r = m;
int i = 1;
while(1){
if(i == index){
if(r <= 0){
puts("YES");
break;
}
r = r - a[i];
l = l - a[i];
}
else {
if(l <= 0){
puts("NO");
break;
}
r = r - a[i];
l--;
}
i++;
if(i > n) i = 1;
}
// if(flag) puts("YES");
// else puts("NO");
}
return 0;
}
Problem F
题解:
二分+ 前缀和 首先预处理出前缀和,pre0[i]表示[1…i]这个区间有多少0; 然后二分答案,再对答案进行O(n)的验证;
只需判断区间内0或1的个数加上m是否不小于当前二分的答案便可; 时间复杂度:O(nlogn)
C++版本一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//std::vector<int> a;
char a[200000+1];
int s1[2000000 + 7],s0[2000000 + 7];
int n;
bool check(int x,int k){
int l = 1, r = x ;
while(r <= n){
if(s0[r] - s0[l-1] <= k || s1[r] - s1[l-1] <= k )
return 1;
l++,r++;
}
return 0;
}
int main(){
int t;
cin >> t;
while(t--){
int m ;
cin >> n >> m;
cin >> a + 1;
int ans = -1,flag = 0;
//cout << strlen(a) << endl;
for(int i = 1; i <= n ; i ++){
s0[i] = s0[i-1];
s1[i] = s1[i-1];
if(a[i] == '0') s0[i] ++;
else s1[i] ++;
}
int l = 1, r = n;
for(int i = 0; i < 100 ; i++){
int mid= (l+r)/2;
//mid 表长度
if(check(mid, m)){
ans = max(mid,ans);
l = mid + 1;
}
else
{
//ans = max(mid,ans);
r = mid - 1;
}
}
cout << ans << endl;
}
}
Problem G
题解:
此问题即是把N个人分为两个集合,且满足以下条件。 i认为j是一个Truthman,那么i和j属于一个集合(一真都真一假都假)
i认为j是一个Fakeman,那么i和j属于不同集合(你真我假你假我真) 要求出符合条件的Truthman最多的一种合理解。
dfs即可,任意找一个没有被分配身份的人开始dfs,假设他的身份为任意一
种,由此可以推出一些人的身份(或者矛盾),因为要使Truthman最多,所以选择人数多的集合作为Truthman(1)即可。
注意预设一个人的身份可能无法推出所有人的身份(图不连通),循环做上面 的步骤即可。 复杂度O(n+m)
C++版本一 种类并查集变形
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define clr(i) memset(i,0,sizeof(i))
inline int read()
{
char c=getchar();int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x;
}
int par[200005],ans[200005];
vector<int> v[200005];
inline int find(int x)
{
return x==par[x]?x:par[x]=find(par[x]);
}
int main()
{
int t=read();
while(t--)
{
clr(ans);
int n=read(),m=read();
for(int i=1;i<=2*n;++i) par[i]=i;
for(int i=1;i<=m;++i)
{
int a=read(),b=read(),c=read();
if(c) par[find(a)]=find(b),par[find(a+n)]=find(b+n);
else par[find(a)]=find(b+n),par[find(b)]=find(a+n);
}
for(int i=1;i<=2*n;++i) v[find(i)].pb(i);
int ok = 1;
for(int i=1;i<=n;++i)
{
if(ans[i]) continue;
int x = find(i);
int cnt1 = 0, cnt2 = 0;
for(int j : v[x]) if(j>n) cnt2++;else cnt1++;
if(cnt1>cnt2)
{
for(int j:v[x])
{
if(j>n&&ans[j-n]) {ok=0;break;}
if(j>n) ans[j-n]=-1;
else ans[j]=1;
}
}
else
{
for(int j:v[x])
{
if(j>n&&ans[j-n]) {ok=0;break;}
if(j>n) ans[j-n]=1;
else ans[j]=-1;
}
}
if(!ok) break;
}
if(!ok) printf("-1\n");
else
{
for(int i=1;i<=n;++i) if(ans[i]>0) printf("1");else printf("0");
printf("\n");
}
for(int i=1;i<=2*n;++i) v[i].clear();
}
}
C++版本二 二分图+建模
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v,w;
int ans,cnt,flag,temp,sum;
int vis[N],VIS[N];
char str;
struct node{
int v,w;
node(){};
node(int _v,int _w):v(_v),w(_w){}
};
vector<node>G[N];
bool dfs(int u,int col){
++sum;
cnt+=col;
vis[u]=col;
bool res=1;
for(int i=0,j=G[u].size();i<j;i++){
int v=G[u][i].v;
int val=G[u][i].w?col:!col;
if(vis[v]==-1){
res&=dfs(v,val);
}else{
res&=val==vis[v];
}
}
return res;
}
void DFS(int u){
vis[u]=!vis[u];
VIS[u]=1;
for(int i=0,j=G[u].size();i<j;i++){
int v=G[u][i].v;
if(!VIS[v])DFS(v);
}
}
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
G[i].clear();
vis[i]=-1;
VIS[i]=0;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
G[u].push_back({v,w});
G[v].push_back({u,w});
}
ans=1;
for(int i=1;i<=n;i++){
if(vis[i]==-1){
sum=cnt=0;
ans&=dfs(i,1);
if(cnt+cnt<sum){
DFS(i);
}
}
}
if(ans){
for(int i=1;i<=n;i++){
cout<<vis[i];
}
cout<<endl;
}else{
cout<<-1<<endl;
}
}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem H
题解:
可以转化为分组背包: 每一天可以选择造成1.2.3…m点生气度,在造成生气度的同时可以省去一些
上线时间。对于每一天mm即可算出造成i点生气度时最多能省去多少时间。 预处理每一天,复杂度O(nmm)
把每一天的每一种生气度当做一个物品,那么生气度就是这个物品的空间, 省去的时间就是这个物品的价值,n个组,每个组m个物品,而背包的大小
就是可以造成生气度的最大值k
做分组背包即可求得最多能省去多少时间,用原来所需要的总时间减去最大省去的时间即是答案,背包复杂度O(nmk),总复杂度O(nmm+nm*k)
C++版本一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[2000000 + 1];
int dp[205],sum[205],mx[205];
char s[205][205];
int main(){
int t ;
cin >> t;
while(t--){
memset(dp,0,sizeof(dp));
int n , m , k;
cin >> n >> m >> k;
for(int i = 1; i <= n ; i ++) cin >> s[i] + 1;
for(int i = 1; i <= n; i++){
memset(mx,0,sizeof(mx));
for(int j = 1 ; j <= m; j++){
sum[j] = s[i][j] -'0' +sum[j-1];
}
mx[sum[m]] = m;
for(int j = 1; j <= m; j++){
for(int w = j; w <= m ; w++){
int tot = sum[m] - sum[w] + sum[j-1];
mx[tot] = max(mx[tot],m-w+j-1);
}
}
for(int j = k; j >= 0; j--){
for(int w = 0; w <= m ; w++){
if(j >= w) dp[j] = max(dp[j],dp[j-w] + mx[w]);
}
}
}
cout << m * n - dp[k] << endl;
}
}
C++版本二
原博客
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PB push_back
#define POP pop_back
#define D double
#define endl '\n'
typedef pair<int,int> PII;
const LL INF=1e18,mod=100000000;
const int N=2e5+7;
int n,m,k;
char a[222];
int s[222][222];//第一维表示天数,第二维表示改天咕女神的小时数,也就是在第i天咕女神j小时获得的最大不在线时间
int f[222];//统计每天女神在线时长的前缀和用的滚动数组
int cnt[222];//女神每天的在线时长
int ans[222][222];//二维背包容器
int main()
{
int t;
cin>>t;
while(t--){
memset(s,0,sizeof(s));
memset(cnt,0,sizeof(cnt));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%s",a+1);
for(int j=1;j<=m;j++){
if(a[j]=='0')f[j]=f[j-1];
else f[j]=f[j-1]+1,cnt[i]++;
}
s[i][cnt[i]]=m;
for(int p=1;p<=m;p++){
for(int q=p;q<=m;q++){
s[i][cnt[i]-f[q]+f[p-1]]=max(s[i][cnt[i]-f[q]+f[p-1]],m-(q-p+1));
}
}
}
int num=m*n;
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++){
for(int j=0;j<=cnt[i];j++){
for(int l=k;l>=j;l--){
ans[i][l]=max(ans[i][l],ans[i-1][l-j]+s[i][j]);
}
}
}
printf("%d\n",num-ans[n][k]);
}
return 0;
}