ACM2020两小时训练补题题解
A - Dice Sum
题意:给定 n, m, k ,分别代表需要的数字数量、从1-m中选,总和要小于等于k,且同一数字可多次重复选择。
正解:背包求解,每次要将一个数字放到背包中。如此循环下去,将所有可能全部求解。
状态:dp[i] [j] 代表 选择了i个数,其总和等于k的序列数量。
初始状态:dp[0] [0]=1;0个数 总和等于0 的序列只有一种情况。
状态转移方程:每次要放进去一个数,背包容量是从1-k,然后看 可以把哪个数放进去,从1-m遍历,如果其值小于等于背包的容量,说明该值可以放到背包中去。
for(int i=1;i<=n;i++){//总共要放入n个数
for(int j=1;j<=k;j++){//背包总容量不能超过k
for(int u=1;u<=m;u++){//可以选择的数的范围是从1-m
if(j>=u){//若是可以将该数放入到背包中
dp[i][j]=(dp[i][j]+dp[i-1][j-u])%mod;//状态转移方程
}
}
}
}
#include <iostream>
#include <cstring>
using namespace std;
typedef long long int ll;
ll mod=998244353;
ll dp[55][2505];
int main() {
int n,m,k;
memset(dp,0,sizeof(dp));
scanf("%d%d%d",&n,&m,&k);
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
for(int u=1;u<=m;u++){
if(j>=u){
dp[i][j]=(dp[i][j]+dp[i-1][j-u])%mod;
}
}
}
}
ll sum=0;
for(int i=1;i<=k;i++){
sum=(sum%mod+dp[n][i]%mod)%mod;
}
cout<<sum<<endl;
}
C - K-colinear Line
题意:给定N个点的x,y坐标,K,求解有多少条不重复的直线,穿过这些点的个数>=min(n,k)。
题解:暴力搜索,三重循环,并标记一条线上重复的点。
知识点:判断两个向量是否平行,可通过向量运算进行,x1*y2==x2 *y1 可判断两条向量平行。
向量n1,n2 夹角判断:n1*n2=|n1| * |n2| *cos ;
#include <iostream>
#include <map>
#include <vector>
#include <set>
using namespace std;
class node{
public:
int x,y;
};
node no[305];
bool fi(int i,int j,int k){
int y1=no[j].y-no[i].y;
int x1=no[j].x-no[i].x;
int y2=no[k].y-no[i].y;
int x2=no[k].x-no[i].x;
if(x1*y2-x2*y1==0)return true;
return false;
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
scanf("%d%d",&no[i].x,&no[i].y);
}
if(k==1){
cout<<"Infinity"<<endl;
return 0;
}
int sum=0;
map<pair<int,int>,bool>mp;
for(int i=0;i<n-1;i++){
for(int j=i+1;j<n;j++){
if(mp.find(make_pair(i,j))!=mp.end())continue;
set<int>st;
bool f=0;
for(int w=j;w<n;w++){
if(fi(i,j,w)){
st.insert(i);
st.insert(j);
st.insert(w);
}
}
int t=st.size();
if(st.size()>=min(n,k)){
sum++;//,cout<<' '<<i<<" "<<j<<endl;
}
set<int>::iterator it1,it2,it3;
it3=st.end();
it3--;
for(it1=st.begin();it1!=it3;it1++){
it2=it1;
it2++;
for(;it2!=st.end();it2++){
mp[make_pair(*it1,*it2)]=1;
}
}
}
}
cout<<sum<<endl;
}